# https://docs.chain.link/chainlink-automation llms-full.txt ## Chainlink Automation [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Chainlink Automation](https://docs.chain.link/chainlink-automation\#overview) Automate your smart contracts using a secure and hyper-reliable decentralized network that uses the same external network of node operators that secures billions in value. Building on Chainlink Automation will accelerate your innovation, save you time and money, and help you get to market faster so you don't have to deal with the setup cost, ongoing maintenance, and risks associated with a centralized automation stack. To learn more about how the Chainlink Automation Network automates your smart contracts, visit the [Concepts](https://docs.chain.link/chainlink-automation/concepts/automation-concepts) and [Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) pages. You can also learn more through our [additional Automation resources](https://chain.link/automation#masterclass). ![](https://docs.chain.link/images/automation/automation_2_diagram_v3.png) ## [Supported networks and costs](https://docs.chain.link/chainlink-automation\#supported-networks-and-costs) For a list of blockchains that are supported by Chainlink Automation, see the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) page. To learn more about the cost of using Chainlink Automation, see the [Automation Economics](https://docs.chain.link/chainlink-automation/overview/automation-economics) page. ## [Contact us](https://docs.chain.link/chainlink-automation\#contact-us) For help with your specific use case, [contact us](https://chain.link/contact?ref_id=Automation) to connect with one of our Solutions Architects. You can also ask questions about Chainlink Automation on [Stack Overflow](https://stackoverflow.com/questions/ask?tags=chainlink) or the [#automation channel](https://discord.com/channels/592041321326182401/821350860302581771) in our [Discord server](https://discord.gg/qj9qarT). For all developers resources, check out the [Developer Resource Hub](https://dev.chain.link/). ## What's next - [\> Getting Started with Automation](https://docs.chain.link/chainlink-automation/overview/getting-started) - [\> Create Automation-Compatible Contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) - [\> Supported Networks for Automation](https://docs.chain.link/chainlink-automation/overview/supported-networks) - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) - [\> Automation Billing and Costs](https://docs.chain.link/chainlink-automation/overview/automation-economics) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Forwarder [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) On this page # [Secure Upkeeps Using the Forwarder](https://docs.chain.link/chainlink-automation/guides/forwarder\#overview) This tutorial explains how to use the `Forwarder` to add additional security to your Automation upkeeps. To learn how other Chainlink Automation contracts work, click [here](https://docs.chain.link/chainlink-automation/reference/automation-contracts). ## [What is a Forwarder? When is it used?](https://docs.chain.link/chainlink-automation/guides/forwarder\#what-is-a-forwarder-when-is-it-used) Each registered upkeep under the Chainlink Automation network has its own unique `Forwarder` contract. The `Forwarder` address becomes available only after upkeep registration, as we deploy a new forwarder for each upkeep. The `Forwarder` contract is the intermediary between the Automation Registry and your Upkeep contract, so you make it the `msg.Sender` for your upkeep. If you don't use the forwarder address, your contract's `performUpkeep` function is open and callable by anyone. If your contract is without risk of accepting unintentional external data, you don't need to use the forwarder address. ## [Securing your upkeep](https://docs.chain.link/chainlink-automation/guides/forwarder\#securing-your-upkeep) If your upkeep's perform function needs to be permissioned, please consider setting `msg.sender` as your forwarder address at the top of your `performUpkeep` function. To make this work you will need to: - Create `forwarder` as a mutable address variable on your contract that only _you_ can update. `forwarder` is a unique value that cannot change for your upkeep. - Create a `setForwarder` function in your contract so you can update the `forwarder` address. - Register your upkeep and then retrieve its forwarder address from the Chainlink Automation App or programmatically. - Call the `setForwarder` function, passing the forwarder address as an input argument. ### [Finding the forwarder address](https://docs.chain.link/chainlink-automation/guides/forwarder\#finding-the-forwarder-address) After you register an upkeep, its forwarder address is available in the Chainlink Automation App. Alternatively, you can fetch it programmatically using `registry.getForwarder(upkeepID)` from the Registry interface. Time-based upkeepsCustom or log trigger upkeeps For time-based upkeeps, do **not** use the listed forwarder address. Instead, use the **Upkeep address** shown in the Chainlink Automation App: ![Upkeep address in Chainlink Automation App](https://docs.chain.link/images/automation/cron-upkeep-address.png) The **Upkeep address** is shown in the middle card of the **Details** section for your upkeep. For custom upkeeps or log trigger upkeeps, use the **Forwarder address** listed in the Chainlink Automation App: ![Forwarder address in Chainlink Automation App](https://docs.chain.link/images/automation/forwarder-address-ui.png) The **Forwarder address** is shown in the left card of the **Details** section for your upkeep. ## [Code example](https://docs.chain.link/chainlink-automation/guides/forwarder\#code-example) The code sample below uses the Forwarder: ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; /** * @dev Example contract which uses the Forwarder * * @notice important to implement {AutomationCompatibleInterface} */ /** * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */ import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol"; import {OwnerIsCreator} from "@chainlink/contracts/src/v0.8/shared/access/OwnerIsCreator.sol"; contract CounterwForwarder is AutomationCompatibleInterface, OwnerIsCreator { uint256 public counter; // counter counts the number of upkeeps performed uint256 public interval; // interval specifies the time between upkeeps uint256 public lastTimeStamp; // lastTimeStamp tracks the last upkeep performed address public s_forwarderAddress; constructor(uint256 updateInterval) { interval = updateInterval; } function checkUpkeep( bytes calldata /*checkData*/ ) external override returns (bool, bytes memory) { bool needsUpkeep = (block.timestamp - lastTimeStamp) > interval; return (needsUpkeep, bytes("")); } function performUpkeep(bytes calldata /*performData*/) external override { require( msg.sender == s_forwarderAddress, "This address does not have permission to call performUpkeep" ); lastTimeStamp = block.timestamp; counter = counter + 1; } /// @notice Set the address that `performUpkeep` is called from /// @dev Only callable by the owner /// @param forwarderAddress the address to set function setForwarderAddress(address forwarderAddress) external onlyOwner { s_forwarderAddress = forwarderAddress; } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/Automation/CounterwForwarder.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) ## What's next - [\> Create Automation-Compatible Contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) - [\> Troubleshoot and Debug Upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Contracts [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Automation Contracts](https://docs.chain.link/chainlink-automation/reference/automation-contracts\#overview) Automation Nodes use the following contracts. You can find them in the [Chainlink repository](https://github.com/smartcontractkit/chainlink/tree/contracts-v1.3.0/contracts/src/v0.8/automation). For details about how to use them, visit the [Creating Compatible Contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) guide. To understand the logic behind these contracts, visit the [Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) page. - [`AutomationCompatible.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/AutomationCompatible.sol): Imports the following contracts: - [`AutomationBase.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/AutomationBase.sol): Enables the use of the `cannotExecute` modifier. Import this contract if you need for this modifier. See the [`checkUpkeep` function](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#checkupkeep-function) for details. - [`AutomationCompatibleInterface.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol): The interface to be implemented in order to make your contract compatible. Import this contract for type safety. ## [AutomationRegistry.sol](https://docs.chain.link/chainlink-automation/reference/automation-contracts\#automationregistrysol) [`AutomationRegistry2_1.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol): The registry contract that tracks all registered Upkeeps and the Automation Nodes that can perform them. _Note_: As Chainlink Automation continues adding new functionalities, a new **Automation Registry** is deployed and the contract address may change. ## [AutomationRegistrar.sol](https://docs.chain.link/chainlink-automation/reference/automation-contracts\#automationregistrarsol) [`AutomationRegistrar2_1.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/v2_1/AutomationRegistrar2_1.sol): The Registrar contract governs the registration of new Upkeeps on the associated `AutomationRegistry` contract. Users who want to register Upkeeps by directly calling the deployed contract have to call the Transfer-and-Call function on the respective ERC-677 LINK contract configured on the Registrar and ensure they pass the correct encoded function call and inputs. ## [UpkeepTranscoder.sol](https://docs.chain.link/chainlink-automation/reference/automation-contracts\#upkeeptranscodersol) [`UpkeepTranscode4_0.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/v2_1/UpkeepTranscoder4_0.sol) allows the conversion of upkeep data from previous Automation registry versions 1.2, 1.3, and 2.0 to registry 2.1. ## [AutomationForwarder.sol](https://docs.chain.link/chainlink-automation/reference/automation-contracts\#automationforwardersol) [`AutomationForwarder.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/AutomationForwarder.sol) is a relayer that sits between the registry and the customer's target contract. The purpose of the forwarder is to give customers a consistent address to authorize against that stays consistent between migrations. The Forwarder also exposes the registry address, so that users who want to programmatically interact with the registry can do so. The `forward` function in this contract is called by the registry and forwards the call to the target. ## [CronUpkeepFactory.sol](https://docs.chain.link/chainlink-automation/reference/automation-contracts\#cronupkeepfactorysol) [`CronUpkeepFactory.sol`](https://github.com/smartcontractkit/chainlink/blob/f6256c3b6ea64a91aadc9ac7df30e9bf35dca557/contracts/src/v0.8/automation/upkeeps/CronUpkeepFactory.sol) serves as a delegate for all instances of `CronUpkeep`. Those contracts delegate their checkUpkeep calls onto this contract. Utilizing this pattern reduces the size of the `CronUpkeep` contracts. You can use this contract when creating a time-based upkeep programmatically. You can learn more about creating upkeeps programmatically [here](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract). ## What's next - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) - [\> Automation Interfaces](https://docs.chain.link/chainlink-automation/reference/automation-interfaces) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Networks [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks\#overview) To use Chainlink Automation on certain networks, you may need to conduct token transfers. You can transfer tokens by using [Chainlink CCIP](https://docs.chain.link/ccip/tutorials/evm/transfer-tokens-from-contract), [Transporter](https://www.transporter.io/) or third-party applications such as [XSwap](https://xswap.link/). ## [Parameters](https://docs.chain.link/chainlink-automation/overview/supported-networks\#parameters) - **Payment Premium %** ( `paymentPremiumPPB`): This percentage premium compensates the Chainlink Automation Network for monitoring and performing your upkeep. Every time a transaction is submitted for your upkeep, your LINK balance is reduced by the transaction cost plus this percentage premium. - **Flat Fee Micro Link** ( `flatFeeMicroLink`): A flat fee charged per transaction on all testnets and OP Mainnet. - **Maximum Check Data Size** ( `maxCheckDataSize`): The maximum size, in bytes, that can be sent to your `checkUpkeep` function. - **Check Gas Limit** ( `checkGasLimit`): The maximum amount of gas that can be used by your `checkUpkeep` function for offchain computation. - **Perform Gas Limit** ( `performGasLimit`): The maximum amount of gas that can be used by the client contract's `performUpkeep` function for the onchain transaction. You can set an upper limit on your upkeep during registration, but this number must not exceed the `maxPerformGas` on the `Registry`. - **maximum Perform Data Size** ( `maxPerformDataSize`): The maximum size in bytes that can be sent to your `performUpkeep` function. - **Gas Ceiling Multiplier** ( `gasCeilingMultiplier`): Establishes a ceiling for the maximum price based on the onchain fast gas feed. - **Minimum Upkeep Spend (LINK)**: The minimum amount of LINK an upkeep must spend over its lifetime. If the lifetime (or total) upkeep spend is below this amount, then at cancellation this amount will be held back. ## [![ARBITRUM icon](https://docs.chain.link/assets/chains/arbitrum.svg)Arbitrum](https://docs.chain.link/chainlink-automation/overview/supported-networks\#arbitrum) ### [Arbitrum One](https://docs.chain.link/chainlink-automation/overview/supported-networks\#arbitrum-one) | Item | Value | | --- | --- | | Registry Address | [0x37D9dC70bfcd8BC77Ec2858836B923c560E891D1](https://explorer.arbitrum.io/address/0x37D9dC70bfcd8BC77Ec2858836B923c560E891D1 "0x37D9dC70bfcd8BC77Ec2858836B923c560E891D1")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x86EFBD0b6736Bed994962f9797049422A3A8E8Ad](https://explorer.arbitrum.io/address/0x86EFBD0b6736Bed994962f9797049422A3A8E8Ad "0x86EFBD0b6736Bed994962f9797049422A3A8E8Ad")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 2,000 | | Gas Ceiling Multiplier | 5 | | Minimum Upkeep Spend (LINK) | 0.1 | ### [Arbitrum Sepolia](https://docs.chain.link/chainlink-automation/overview/supported-networks\#arbitrum-sepolia) | Item | Value | | --- | --- | | Registry Address | [0x8194399B3f11fcA2E8cCEfc4c9A658c61B8Bf412](https://sepolia.arbiscan.io/address/0x8194399B3f11fcA2E8cCEfc4c9A658c61B8Bf412 "0x8194399B3f11fcA2E8cCEfc4c9A658c61B8Bf412")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x881918E24290084409DaA91979A30e6f0dB52eBe](https://sepolia.arbiscan.io/address/0x881918E24290084409DaA91979A30e6f0dB52eBe "0x881918E24290084409DaA91979A30e6f0dB52eBe")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 2,000 | | Gas Ceiling Multiplier | 5 | | Minimum Upkeep Spend (LINK) | 0.1 | ## [![AVALANCHE icon](https://docs.chain.link/assets/chains/avalanche.svg)Avalanche](https://docs.chain.link/chainlink-automation/overview/supported-networks\#avalanche) ### [Avalanche](https://docs.chain.link/chainlink-automation/overview/supported-networks\#avalanche-1) | Item | Value | | --- | --- | | Registry Address | [0x7f00a3Cd4590009C349192510D51F8e6312E08CB](https://snowtrace.io/address/0x7f00a3Cd4590009C349192510D51F8e6312E08CB "0x7f00a3Cd4590009C349192510D51F8e6312E08CB")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x5Cb7B29e621810Ce9a04Bee137F8427935795d00](https://snowtrace.io/address/0x5Cb7B29e621810Ce9a04Bee137F8427935795d00 "0x5Cb7B29e621810Ce9a04Bee137F8427935795d00")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 40 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.1 | ### [Avalanche Fuji](https://docs.chain.link/chainlink-automation/overview/supported-networks\#avalanche-fuji) | Item | Value | | --- | --- | | Registry Address | [0x819B58A646CDd8289275A87653a2aA4902b14fe6](https://testnet.snowtrace.io/address/0x819B58A646CDd8289275A87653a2aA4902b14fe6 "0x819B58A646CDd8289275A87653a2aA4902b14fe6")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0xD23D3D1b81711D75E1012211f1b65Cc7dBB474e2](https://testnet.snowtrace.io/address/0xD23D3D1b81711D75E1012211f1b65Cc7dBB474e2 "0xD23D3D1b81711D75E1012211f1b65Cc7dBB474e2")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 40 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.0 | ## [![BASE icon](https://docs.chain.link/assets/chains/base.svg)Base](https://docs.chain.link/chainlink-automation/overview/supported-networks\#base) ### [Base](https://docs.chain.link/chainlink-automation/overview/supported-networks\#base-1) | Item | Value | | --- | --- | | Registry Address | [0xf4bAb6A129164aBa9B113cB96BA4266dF49f8743](https://basescan.org/address/0xf4bAb6A129164aBa9B113cB96BA4266dF49f8743 "0xf4bAb6A129164aBa9B113cB96BA4266dF49f8743")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0xE28Adc50c7551CFf69FCF32D45d037e5F6554264](https://basescan.org/address/0xE28Adc50c7551CFf69FCF32D45d037e5F6554264 "0xE28Adc50c7551CFf69FCF32D45d037e5F6554264")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 2,000 | | Gas Ceiling Multiplier | 5 | | Minimum Upkeep Spend (LINK) | 0.1 | ### [Base Sepolia](https://docs.chain.link/chainlink-automation/overview/supported-networks\#base-sepolia) | Item | Value | | --- | --- | | Registry Address | [0x91D4a4C3D448c7f3CB477332B1c7D420a5810aC3](https://sepolia.basescan.org/address/0x91D4a4C3D448c7f3CB477332B1c7D420a5810aC3 "0x91D4a4C3D448c7f3CB477332B1c7D420a5810aC3")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0xf28D56F3A707E25B71Ce529a21AF388751E1CF2A](https://sepolia.basescan.org/address/0xf28D56F3A707E25B71Ce529a21AF388751E1CF2A "0xf28D56F3A707E25B71Ce529a21AF388751E1CF2A")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 1,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.1 | ## [![BNB icon](https://docs.chain.link/assets/chains/bnb-chain.svg)BNB Chain](https://docs.chain.link/chainlink-automation/overview/supported-networks\#bnb-chain) ### [BNB Chain](https://docs.chain.link/chainlink-automation/overview/supported-networks\#bnb-chain-1) | Item | Value | | --- | --- | | Registry Address | [0xDc21E279934fF6721CaDfDD112DAfb3261f09A2C](https://bscscan.com/address/0xDc21E279934fF6721CaDfDD112DAfb3261f09A2C "0xDc21E279934fF6721CaDfDD112DAfb3261f09A2C")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0xf671F60bCC964B309D22424886FF202807381B32](https://bscscan.com/address/0xf671F60bCC964B309D22424886FF202807381B32 "0xf671F60bCC964B309D22424886FF202807381B32")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 30 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 3 | | Minimum Upkeep Spend (LINK) | 0.1 | ### [BNB Chain Testnet](https://docs.chain.link/chainlink-automation/overview/supported-networks\#bnb-chain-testnet) | Item | Value | | --- | --- | | Registry Address | [0x96bb60aAAec09A0FceB4527b81bbF3Cc0c171393](https://testnet.bscscan.com/address/0x96bb60aAAec09A0FceB4527b81bbF3Cc0c171393 "0x96bb60aAAec09A0FceB4527b81bbF3Cc0c171393")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x0631ea498c2Cd8371B020b9eC03f5F779174562B](https://testnet.bscscan.com/address/0x0631ea498c2Cd8371B020b9eC03f5F779174562B "0x0631ea498c2Cd8371B020b9eC03f5F779174562B")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 30 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 3 | | Minimum Upkeep Spend (LINK) | 0.0 | ## [![ETHEREUM icon](https://docs.chain.link/assets/chains/ethereum.svg)Ethereum](https://docs.chain.link/chainlink-automation/overview/supported-networks\#ethereum) ### [Ethereum](https://docs.chain.link/chainlink-automation/overview/supported-networks\#ethereum-1) | Item | Value | | --- | --- | | Registry Address | [0x6593c7De001fC8542bB1703532EE1E5aA0D458fD](https://etherscan.io/address/0x6593c7De001fC8542bB1703532EE1E5aA0D458fD "0x6593c7De001fC8542bB1703532EE1E5aA0D458fD")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x6B0B234fB2f380309D47A7E9391E29E9a179395a](https://etherscan.io/address/0x6B0B234fB2f380309D47A7E9391E29E9a179395a "0x6B0B234fB2f380309D47A7E9391E29E9a179395a")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 20 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 2,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.1 | ### [Ethereum Sepolia](https://docs.chain.link/chainlink-automation/overview/supported-networks\#ethereum-sepolia) | Item | Value | | --- | --- | | Registry Address | [0x86EFBD0b6736Bed994962f9797049422A3A8E8Ad](https://sepolia.etherscan.io/address/0x86EFBD0b6736Bed994962f9797049422A3A8E8Ad "0x86EFBD0b6736Bed994962f9797049422A3A8E8Ad")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0xb0E49c5D0d05cbc241d68c05BC5BA1d1B7B72976](https://sepolia.etherscan.io/address/0xb0E49c5D0d05cbc241d68c05BC5BA1d1B7B72976 "0xb0E49c5D0d05cbc241d68c05BC5BA1d1B7B72976")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 20 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 2,000 | | Gas Ceiling Multiplier | 3 | | Minimum Upkeep Spend (LINK) | 0.0 | ## [![FANTOM icon](https://docs.chain.link/assets/chains/fantom.svg)Fantom](https://docs.chain.link/chainlink-automation/overview/supported-networks\#fantom) ### [Fantom](https://docs.chain.link/chainlink-automation/overview/supported-networks\#fantom-1) | Item | Value | | --- | --- | | Registry Address | [0x02777053d6764996e594c3E88AF1D58D5363a2e6](https://ftmscan.com/address/0x02777053d6764996e594c3E88AF1D58D5363a2e6 "0x02777053d6764996e594c3E88AF1D58D5363a2e6")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0xDb8e8e2ccb5C033938736aa89Fe4fa1eDfD15a1d](https://ftmscan.com/address/0xDb8e8e2ccb5C033938736aa89Fe4fa1eDfD15a1d "0xDb8e8e2ccb5C033938736aa89Fe4fa1eDfD15a1d")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | 50 | | Maximum Check Data Size | Not Applicable | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 3,500,000 | | Maximum Perform Data Size | Not Applicable | | Gas Ceiling Multiplier | 4 | | Minimum Upkeep Spend (LINK) | 0.1 | ### [Fantom Testnet](https://docs.chain.link/chainlink-automation/overview/supported-networks\#fantom-testnet) | Item | Value | | --- | --- | | Registry Address | [0x8Ef7AC62dc3a4FF4dcc0441ed098106f8F313220](https://testnet.ftmscan.com/address/0x8Ef7AC62dc3a4FF4dcc0441ed098106f8F313220 "0x8Ef7AC62dc3a4FF4dcc0441ed098106f8F313220")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x57A4a13b35d25EE78e084168aBaC5ad360252467](https://testnet.ftmscan.com/address/0x57A4a13b35d25EE78e084168aBaC5ad360252467 "0x57A4a13b35d25EE78e084168aBaC5ad360252467")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | 200 | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 3,500,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.0 | ## [![GNOSIS icon](https://docs.chain.link/assets/chains/gnosis-chain.svg)Gnosis](https://docs.chain.link/chainlink-automation/overview/supported-networks\#gnosis) ### [Gnosis](https://docs.chain.link/chainlink-automation/overview/supported-networks\#gnosis-1) | Item | Value | | --- | --- | | Registry Address | [0x299c92a219F61a82E91d2062A262f7157F155AC1](https://gnosis.blockscout.com/address/0x299c92a219F61a82E91d2062A262f7157F155AC1 "0x299c92a219F61a82E91d2062A262f7157F155AC1")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32](https://gnosis.blockscout.com/address/0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32 "0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 100 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 3 | | Minimum Upkeep Spend (LINK) | 0.1 | ### [Gnosis Chiado](https://docs.chain.link/chainlink-automation/overview/supported-networks\#gnosis-chiado) | Item | Value | | --- | --- | | Registry Address | [0x2CA3BC9eC81E9647e7f8e7EdFE630a27A4E470dB](https://gnosis-chiado.blockscout.com/address/0x2CA3BC9eC81E9647e7f8e7EdFE630a27A4E470dB "0x2CA3BC9eC81E9647e7f8e7EdFE630a27A4E470dB")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0xcfB98e8E3AB99217a0E61C29f86ba3a4B79037BF](https://gnosis-chiado.blockscout.com/address/0xcfB98e8E3AB99217a0E61C29f86ba3a4B79037BF "0xcfB98e8E3AB99217a0E61C29f86ba3a4B79037BF")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 30 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 3 | | Minimum Upkeep Spend (LINK) | 0.1 | ## [![OP icon](https://docs.chain.link/assets/chains/optimism.svg)OP](https://docs.chain.link/chainlink-automation/overview/supported-networks\#op) ### [OP Mainnet](https://docs.chain.link/chainlink-automation/overview/supported-networks\#op-mainnet) | Item | Value | | --- | --- | | Registry Address | [0x696fB0d7D069cc0bb35a7c36115CE63E55cb9AA6](https://optimistic.etherscan.io/address/0x696fB0d7D069cc0bb35a7c36115CE63E55cb9AA6 "0x696fB0d7D069cc0bb35a7c36115CE63E55cb9AA6")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0xe601C5837307f07aB39DEB0f5516602f045BF14f](https://optimistic.etherscan.io/address/0xe601C5837307f07aB39DEB0f5516602f045BF14f "0xe601C5837307f07aB39DEB0f5516602f045BF14f")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 1,000 | | Gas Ceiling Multiplier | 5 | | Minimum Upkeep Spend (LINK) | 0.02 | ### [OP Sepolia](https://docs.chain.link/chainlink-automation/overview/supported-networks\#op-sepolia) | Item | Value | | --- | --- | | Registry Address | [0x881918E24290084409DaA91979A30e6f0dB52eBe](https://sepolia-optimism.etherscan.io/address/0x881918E24290084409DaA91979A30e6f0dB52eBe "0x881918E24290084409DaA91979A30e6f0dB52eBe")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x110Bd89F0B62EA1598FfeBF8C0304c9e58510Ee5](https://sepolia-optimism.etherscan.io/address/0x110Bd89F0B62EA1598FfeBF8C0304c9e58510Ee5 "0x110Bd89F0B62EA1598FfeBF8C0304c9e58510Ee5")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 2,000 | | Gas Ceiling Multiplier | 5 | | Minimum Upkeep Spend (LINK) | 0.1 | ## [![POLYGON icon](https://docs.chain.link/assets/chains/polygon.svg)Polygon](https://docs.chain.link/chainlink-automation/overview/supported-networks\#polygon) ### [Polygon](https://docs.chain.link/chainlink-automation/overview/supported-networks\#polygon-1) | Item | Value | | --- | --- | | Registry Address | [0x08a8eea76D2395807Ce7D1FC942382515469cCA1](https://polygonscan.com/address/0x08a8eea76D2395807Ce7D1FC942382515469cCA1 "0x08a8eea76D2395807Ce7D1FC942382515469cCA1")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x0Bc5EDC7219D272d9dEDd919CE2b4726129AC02B](https://polygonscan.com/address/0x0Bc5EDC7219D272d9dEDd919CE2b4726129AC02B "0x0Bc5EDC7219D272d9dEDd919CE2b4726129AC02B")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 70 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 3 | | Minimum Upkeep Spend (LINK) | 0.1 | ### [Polygon Amoy](https://docs.chain.link/chainlink-automation/overview/supported-networks\#polygon-amoy) | Item | Value | | --- | --- | | Registry Address | [0x93C0e201f7B158F503a1265B6942088975f92ce7](https://amoy.polygonscan.com//address/0x93C0e201f7B158F503a1265B6942088975f92ce7 "0x93C0e201f7B158F503a1265B6942088975f92ce7")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x99083A4bb154B0a3EC7a0D1eb40370C892Db4225](https://amoy.polygonscan.com//address/0x99083A4bb154B0a3EC7a0D1eb40370C892Db4225 "0x99083A4bb154B0a3EC7a0D1eb40370C892Db4225")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 30 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 3 | | Minimum Upkeep Spend (LINK) | 0.1 | ## [![POLYGON_ZKEVM icon](https://docs.chain.link/assets/chains/polygonzkevm.svg)Polygon zkEVM](https://docs.chain.link/chainlink-automation/overview/supported-networks\#polygon-zkevm) ### [Polygon zkEVM](https://docs.chain.link/chainlink-automation/overview/supported-networks\#polygon-zkevm-1) | Item | Value | | --- | --- | | Registry Address | [0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32](https://zkevm.polygonscan.com/address/0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32 "0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x703C1d261a996755409c74d00871e7D6Af4d9896](https://zkevm.polygonscan.com/address/0x703C1d261a996755409c74d00871e7D6Af4d9896 "0x703C1d261a996755409c74d00871e7D6Af4d9896")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 56 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.0004 | ### [Polygon zkEVM Cardona](https://docs.chain.link/chainlink-automation/overview/supported-networks\#polygon-zkevm-cardona) | Item | Value | | --- | --- | | Registry Address | [0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32](https://cardona-zkevm.polygonscan.com/address/0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32 "0x0F7E163446AAb41DB5375AbdeE2c3eCC56D9aA32")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x703C1d261a996755409c74d00871e7D6Af4d9896](https://cardona-zkevm.polygonscan.com/address/0x703C1d261a996755409c74d00871e7D6Af4d9896 "0x703C1d261a996755409c74d00871e7D6Af4d9896")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.0004 | ## [![SCROLL icon](https://docs.chain.link/assets/chains/scroll.svg)Scroll](https://docs.chain.link/chainlink-automation/overview/supported-networks\#scroll) ### [Scroll](https://docs.chain.link/chainlink-automation/overview/supported-networks\#scroll-1) | Item | Value | | --- | --- | | Registry Address | [0xBe55E7eb27Cd69Be0883E0284632A91bB7AdC272](https://scrollscan.com/address/0xBe55E7eb27Cd69Be0883E0284632A91bB7AdC272 "0xBe55E7eb27Cd69Be0883E0284632A91bB7AdC272")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x80C55e674a34FfE730B0357E16e8852B19573f7C](https://scrollscan.com/address/0x80C55e674a34FfE730B0357E16e8852B19573f7C "0x80C55e674a34FfE730B0357E16e8852B19573f7C")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 56 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.0004 | ### [Scroll Sepolia](https://docs.chain.link/chainlink-automation/overview/supported-networks\#scroll-sepolia) | Item | Value | | --- | --- | | Registry Address | [0x93C0e201f7B158F503a1265B6942088975f92ce7](https://sepolia.scrollscan.dev/address/0x93C0e201f7B158F503a1265B6942088975f92ce7 "0x93C0e201f7B158F503a1265B6942088975f92ce7")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Registrar Address | [0x8ee44ab698169a0AcA2571834b19a02d09D818d5](https://sepolia.scrollscan.dev/address/0x8ee44ab698169a0AcA2571834b19a02d09D818d5 "0x8ee44ab698169a0AcA2571834b19a02d09D818d5")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) | | Payment Premium % | 50 | | Block Count per Turn | Not Applicable | | Maximum Check Data Size | 5,000 | | Check Gas Limit | 10,000,000 | | Perform Gas Limit | 5,000,000 | | Maximum Perform Data Size | 5,000 | | Gas Ceiling Multiplier | 2 | | Minimum Upkeep Spend (LINK) | 0.1 | ## What's next - [\> Register Time-based Upkeeps](https://docs.chain.link/chainlink-automation/guides/job-scheduler) - [\> Register Custom Logic Upkeeps](https://docs.chain.link/chainlink-automation/guides/register-upkeep) - [\> Register Log Trigger Upkeeps](https://docs.chain.link/chainlink-automation/guides/log-trigger) - [\> Create Automation-Compatible Contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) - [\> Automation Billing and Costs](https://docs.chain.link/chainlink-automation/overview/automation-economics) ## Get the latest Chainlink content straight to your inbox. Email Address ## Register Custom Logic Upkeep [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Register a Custom Logic Upkeep](https://docs.chain.link/chainlink-automation/guides/register-upkeep\#overview) Create powerful automation for your smart contract that leverages custom logic to trigger specified actions. This guide explains how to register a custom logic upkeep that uses a [compatible contract](https://docs.chain.link/chainlink-automation/guides/compatible-contracts). You can register it using the Chainlink Automation App or from within a contract that you deploy. ## [Using the Chainlink Automation App](https://docs.chain.link/chainlink-automation/guides/register-upkeep\#using-the-chainlink-automation-app) [Open the Chainlink Automation App](https://automation.chain.link/) **Click the Register New Upkeep button** ![](https://docs.chain.link/images/automation/auto-ui-home.png) ### [Connecting your wallet](https://docs.chain.link/chainlink-automation/guides/register-upkeep\#connecting-your-wallet) If you do not already have a wallet connected with the Chainlink Automation network, the interface will prompt you to do so. Click the **Connect Wallet** button and follow the remaining prompts to connect your wallet to one of the [Automation supported blockchain networks](https://docs.chain.link/chainlink-automation/overview/supported-networks). ![](https://docs.chain.link/images/automation/auto-ui-wallet.png) ## [Trigger selection](https://docs.chain.link/chainlink-automation/guides/register-upkeep\#trigger-selection) Select **Custom Logic** trigger. ![](https://docs.chain.link/images/automation/ui_select_trigger.png) ## [Using custom logic triggers](https://docs.chain.link/chainlink-automation/guides/register-upkeep\#using-custom-logic-triggers) Provide the address of your [compatible contract](https://docs.chain.link/chainlink-automation/guides/compatible-contracts). You do not need to verify the contract onchain, but it must be [compatible](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) with the `AutomationCompatibleInterface` contract. ## [Entering upkeep details](https://docs.chain.link/chainlink-automation/guides/register-upkeep\#entering-upkeep-details) Provide the following information in the Automation app: - **Upkeep name**: This will be publicly visible in the Chainlink Automation app. - **Gas limit**: This is the maximum amount of gas that your transaction requires to execute on chain. This limit cannot exceed the `performGasLimit` value configured on the [registry](https://docs.chain.link/chainlink-automation/overview/supported-networks). Before the network executes your transaction on chain, it simulates the transaction. If the gas required to execute your transaction exceeds the gas limit that you specified, your transaction will not be confirmed. Developers also have the ability to update `performGasLimit` for an upkeep. Consider running your function on a testnet to see how much gas it uses before you select a gas limit. This can be changed afterwards. - **Starting balance (LINK)**: Specify a LINK starting balance to fund your upkeep. See the [LINK Token Contracts](https://docs.chain.link/resources/link-token-contracts) page to find the correct contract address and access faucets for testnet LINK. This field is required. You must have LINK before you can use the Chainlink Automation service. - **Check data**: This field is provided as an input for when your `checkUpkeep` function is simulated. Either leave this field blank or specify a hexadecimal value starting with `0x`. To learn how to make flexible upkeeps using `checkData`, see the [Flexible Upkeeps](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps) guide. - **Your email address (optional)**: This email address will be used to send you an email notification when your upkeep is underfunded. ## [Complete upkeep registration](https://docs.chain.link/chainlink-automation/guides/register-upkeep\#complete-upkeep-registration) Click **Register upkeep** and confirm the transaction in MetaMask. ![Upkeep Registration Success Message](https://docs.chain.link/images/automation/automation-registration-submitted.png) Your upkeeps will be displayed in your list of **Active Upkeeps**. You must monitor the balance of your upkeep. If the balance drops below the **minimum balance**, the Chainlink Automation Network will not perform the Upkeep. See [Managing Upkeeps](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps) to learn how to manage your upkeeps. ## What's next - [\> Register Log Trigger Upkeeps](https://docs.chain.link/chainlink-automation/guides/log-trigger/) - [\> Register Upkeeps Programmatically](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract/) - [\> Automation Interfaces](https://docs.chain.link/chainlink-automation/reference/automation-interfaces) - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture/) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Economics [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Automation Billing and Costs](https://docs.chain.link/chainlink-automation/overview/automation-economics\#overview) ## [Cost of using Chainlink Automation](https://docs.chain.link/chainlink-automation/overview/automation-economics\#cost-of-using-chainlink-automation) Chainlink Automation requires only an execution fee for transactions onchain. This fee includes the transaction cost, a node operator percentage fee, and a small fixed gas overhead accounting for gas between the network and the registry. The percentage fee compensates the Automation Network for monitoring and performing your upkeep. The Automation percentage fee varies by chain and is listed on our [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) page. The fee calculation for upkeeps that are configured for LINK payments (or other non-native payments as applicable) have a conversion from native tokens to LINK (or other non-native payments as applicable). This conversion is not performed in the fee calculation for upkeeps that are configured for native token payments. Otherwise, the fee formula is the same for both payment types: LINKNative tokens **Formula for Registry v2.3** FeeLINK = \[tx.gasPriceNative WEI \\* gasUsed \* (1 + premium%) + (gasOverhead \* tx.gasPriceNative WEI)\]/\[LINK/NativeRate in WEI\] **Formula for Registry v2.3** FeeNative = tx.gasPriceNative WEI \\* gasUsed \* (1 + premium%) + (gasOverhead \* tx.gasPriceNative WEI) There is no registration fee or other fees for any offchain computation. ### [Fee calculation example](https://docs.chain.link/chainlink-automation/overview/automation-economics\#fee-calculation-example) An upkeep transaction was [performed](https://polygonscan.com/tx/0x19309782e15952c90dcadcc02cbf34f331daaeee369d3e3acca43bade02af105) on _Polygon mainnet_. It used _110,051_ gas at a gas price of _182,723,799,380 wei_. The node operator percentage on Polygon was _70%_ at the time of this transaction, and this fee [varies by network](https://docs.chain.link/chainlink-automation/overview/supported-networks). The tables below show how the fees are calculated for upkeeps configured for LINK payments: The LINK/MATIC exchange rate for this transaction was _7,308,290,731,273,610,000 wei_. The upkeep's LINK balance was reduced by a fee of _0.008077 LINK_. The preceding information and calculation can be found in the table below: | Variable | Description | Value | | --- | --- | --- | | tx.gasPriceNative WEI | Gas price of the transaction | 182, 723, 799,380 | | gasUsed | Gas used for performUpkeep calculated in solidity | 110,051 | | gasOverhead | Fixed gas amount used for transaction call from node to Registry | 80,000 | | premium% | Current premium on Polygon which can be found on the [Supported Networks page](https://docs.chain.link/chainlink-automation/overview/supported-networks) | 70% | | LINK/NativeRate in WEI | Exchange rate fetched from Chainlink Oracle | 7,308,290,731,273,610,000 | 0.008077 = \[182,723,799,380 \* (110,051 + 80,000) \* (1 + 70%)\]/\[7,308,290,731,273,610,000\] ## [How funding works](https://docs.chain.link/chainlink-automation/overview/automation-economics\#how-funding-works) Upkeeps have a LINK (ERC-677) balance. Every time an onchain transaction is performed for your upkeep, its LINK balance will be reduced by the LINK fee. Your upkeep's balance must exceed the [minimum balance](https://docs.chain.link/chainlink-automation/overview/automation-economics#minimum-balance). If this requirement is not met, the Automation Network will not perform onchain transactions. You can add funds using the [Chainlink Automation App](https://automation.chain.link/) or by directly calling the `addFunds()` function on the `AutomationRegistry` contract. Anyone can call the `addFunds()` function. ## [Withdrawing funds](https://docs.chain.link/chainlink-automation/overview/automation-economics\#withdrawing-funds) To withdraw a LINK balance, you must cancel your upkeep first. Any upkeep that has not spent more than an aggregated amount of 0.1 LINK fees over the span of its lifetime is subject to a _0.1 LINK_ fee. This cancellation fee protects node operators from spammers who register jobs that never perform. **Example 1**: Your upkeep has spent _4.8 LINK_ over its lifetime and has a balance of _5 LINK_. When it is cancelled, I will receive _5 LINK_. **Example 2**: Your upkeep has spent _0 LINK_ over its lifetime and has a balance of _5 LINK_. When it is cancelled, I will receive _4.9 LINK_. ## [No node competition](https://docs.chain.link/chainlink-automation/overview/automation-economics\#no-node-competition) Individual Automation Nodes do not compete with one another, but rather work together to ensure all registered upkeeps are performed. This makes costs more predictable upfront, enabling you to estimate costs based on the expected gas consumption. ## [Minimum balance](https://docs.chain.link/chainlink-automation/overview/automation-economics\#minimum-balance) The Chainlink Automation Network is designed to perform your upkeep even when gas prices spike. The minimum balance in LINK reflects the best estimate of the cost to perform your upkeep when gas prices spike. To ensure your upkeep is monitored and performed, ensure that your upkeep's balance is above this minimum balance. The minimum balance is calculated using the current fast gas price, the gas limit you entered for your upkeep, the max gas multiplier, and the LINK/NativeRate in WEI for conversion to LINK. To find the latest value for the `gasCeilingMultiplier`, see the [Registry Configuration](https://docs.chain.link/chainlink-automation/overview/supported-networks) page. Follow [maintain a minimum balance](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps/#maintain-a-minimum-balance) to ensure that your upkeep is funded. ## [Price selection and gas bumping](https://docs.chain.link/chainlink-automation/overview/automation-economics\#price-selection-and-gas-bumping) Automation Nodes select the gas price dynamically based on the prices of transactions within the last several blocks. This optimizes the gas price based on current network conditions. Automation Nodes are configured to select a price based on a target percentile. If the Automation Node does not see the `performUpkeep` transaction get confirmed within the next few blocks, it automatically replaces the transaction and bumps the gas price. This process repeats until the transaction is confirmed. ## [ERC-677 LINK](https://docs.chain.link/chainlink-automation/overview/automation-economics\#erc-677-link) For funding on mainnet, you will need ERC-677 LINK. Many token bridges give you ERC-20 LINK tokens. Use PegSwap to [convert Chainlink tokens (LINK) to be ERC-677 compatible](https://pegswap.chain.link/). Use [faucets.chain.link](https://faucets.chain.link/) to get testnet LINK. ## What's next - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) - [\> Supported Networks for Automation](https://docs.chain.link/chainlink-automation/overview/supported-networks) ## Get the latest Chainlink content straight to your inbox. Email Address [iframe](https://td.doubleclick.net/td/rul/346357746?random=1747983692550&cv=11&fst=1747983692550&fmt=3&bg=ffffff&guid=ON&async=1&gcl_ctr=1>m=45be55l1v891173849z8847174275za201zb847174275&gcd=13l3l3l3l1l1&dma=0&tag_exp=101509157~103116026~103130495~103130497~103200004~103233427~103252644~103252646~103301114~103301116~104481633~104481635&ptag_exp=101509157~103116026~103130495~103130497~103200004~103233427~103252644~103252646~103301114~103301116~104481633~104481635&u_w=1280&u_h=1024&url=https%3A%2F%2Fdocs.chain.link%2Fchainlink-automation%2Foverview%2Fautomation-economics&label=Jy3XCMzZ-oYYEPL_k6UB&hn=www.googleadservices.com&frm=0&tiba=Automation%20Billing%20and%20Costs%20%7C%20Chainlink%20Documentation&value=0&bttype=purchase&npa=0&pscdl=noapi&auid=394473314.1747983692&uaa=x86&uab=64&uafvl=Chromium%3B136.0.7103.113%7CGoogle%2520Chrome%3B136.0.7103.113%7CNot.A%252FBrand%3B99.0.0.0&uamb=0&uam=&uap=Linux%20x86_64&uapv=6.6.72&uaw=0&fledge=1&capi=1&_tu=Cg&ct_cookie_present=0)[iframe](https://td.doubleclick.net/td/rul/346357746?random=1747983692584&cv=11&fst=1747983692584&fmt=3&bg=ffffff&guid=ON&async=1&gcl_ctr=2>m=45be55l1v891173849z8847174275za201zb847174275&gcd=13l3l3l3l1l1&dma=0&tag_exp=101509157~103116026~103130495~103130497~103200004~103233427~103252644~103252646~103301114~103301116~104481633~104481635&ptag_exp=101509157~103116026~103130495~103130497~103200004~103233427~103252644~103252646~103301114~103301116~104481633~104481635&u_w=1280&u_h=1024&url=https%3A%2F%2Fdocs.chain.link%2Fchainlink-automation%2Foverview%2Fautomation-economics&label=_duuCKn_k4cYEPL_k6UB&hn=www.googleadservices.com&frm=0&tiba=Automation%20Billing%20and%20Costs%20%7C%20Chainlink%20Documentation&value=0&bttype=purchase&npa=0&pscdl=noapi&auid=394473314.1747983692&uaa=x86&uab=64&uafvl=Chromium%3B136.0.7103.113%7CGoogle%2520Chrome%3B136.0.7103.113%7CNot.A%252FBrand%3B99.0.0.0&uamb=0&uam=&uap=Linux%20x86_64&uapv=6.6.72&uaw=0&fledge=1&capi=1&_tu=Cg&ct_cookie_present=0) ## Automation-Compatible Contracts Guide [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Create Automation-Compatible Contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts\#overview) Learn how to make smart contracts that are compatible with `Automation`. ## [Automation compatible contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts\#automation-compatible-contracts) A contract is Automation-compatible when it follows a specified interface that allows the Chainlink Automation Network to determine if, when, and how the contract should be automated. The interface you use will depend on the type of trigger you want to use: - If you want a log event to trigger your upkeep, use the [`ILogAutomation`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces/#ilogautomation) interface. - If you want to use onchain state in a custom calculation to trigger your upkeep, use [`AutomationCompatibleInterface`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces/#automationcompatibleinterface) interface. - If you want to call a function just based on time, you don't need an interface. Consider instead using a [time-based upkeep](https://docs.chain.link/chainlink-automation/guides/job-scheduler). - If you want to use Automation with Data Streams, use [`StreamsLookupCompatibleInterface`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces/#streamslookupcompatibleinterface) interface. You can learn more about these interfaces [here](https://docs.chain.link/chainlink-automation/reference/automation-interfaces). ## [Example Automation-compatible contract using custom logic trigger](https://docs.chain.link/chainlink-automation/guides/compatible-contracts\#example-automation-compatible-contract-using-custom-logic-trigger) Custom logic Automation compatible contracts must meet the following requirements: - Import `AutomationCompatible.sol`. You can refer to the [Chainlink Contracts](https://github.com/smartcontractkit/chainlink/tree/contracts-v1.3.0/contracts/src) on GitHub to find the latest version. - Use the [`AutomationCompatibleInterface`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces) from the library to ensure your `checkUpkeep` and `performUpkeep` function definitions match the definitions expected by the Chainlink Automation Network. - Include a `checkUpkeep` function that contains the logic that will be executed offchain to see if `performUpkeep` should be executed. `checkUpkeep` can use onchain data and a specified `checkData` parameter to perform complex calculations offchain and then send the result to `performUpkeep` as `performData`. - Include a `performUpkeep` function that will be executed onchain when `checkUpkeep` returns `true`. Use these elements to create a compatible contract that will automatically increment a counter after every `updateInterval` seconds. After you register the contract as an upkeep, the Chainlink Automation Network frequently simulates your `checkUpkeep` offchain to determine if the `updateInterval` time has passed since the last increment (timestamp). When `checkUpkeep` returns true, the Chainlink Automation Network calls `performUpkeep` onchain and increments the counter. This cycle repeats until the upkeep is cancelled or runs out of funding. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; // AutomationCompatible.sol imports the functions from both ./AutomationBase.sol and // ./interfaces/AutomationCompatibleInterface.sol import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol"; /** * @dev Example contract, use the Forwarder as needed for additional security. * * @notice important to implement {AutomationCompatibleInterface} */ /** * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */ contract Counter is AutomationCompatibleInterface { /** * Public counter variable */ uint256 public counter; /** * Use an interval in seconds and a timestamp to slow execution of Upkeep */ uint256 public immutable interval; uint256 public lastTimeStamp; constructor(uint256 updateInterval) { interval = updateInterval; lastTimeStamp = block.timestamp; counter = 0; } function checkUpkeep( bytes calldata /* checkData */ ) external view override returns (bool upkeepNeeded, bytes memory /* performData */) { upkeepNeeded = (block.timestamp - lastTimeStamp) > interval; // We don't use the checkData in this example. The checkData is defined when the Upkeep was registered. } function performUpkeep(bytes calldata /* performData */) external override { if ((block.timestamp - lastTimeStamp) > interval) { lastTimeStamp = block.timestamp; counter = counter + 1; } // We don't use the performData in this example. The performData is generated by the Automation Node's call to your checkUpkeep function } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/Automation/AutomationCounter.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) Compile and deploy your own Automation Counter onto a [supported Testnet](https://docs.chain.link/chainlink-automation/overview/supported-networks). 1. In the Remix example, select the compile tab on the left and press the compile button. Make sure that your contract compiles without any errors. Note that the Warning messages in this example are acceptable and will not block the deployment. 2. Select the **Deploy** tab and deploy the `Counter` smart contract in the `injected web3` environment. When deploying the contract, specify the `updateInterval` value. For this example, set a short interval of 60. This is the interval at which the `performUpkeep` function will be called. 3. After deployment is complete, copy the address of the deployed contract. This address is required to register your upkeep in the [Automation UI](https://automation.chain.link/). The example in this document uses [custom logic](https://docs.chain.link/chainlink-automation/guides/register-upkeep) automation. To see more complex examples, go to the [Quick Starts](https://docs.chain.link/quickstarts/eth-balance-monitor/) page. Now register your [upkeep](https://docs.chain.link/chainlink-automation/guides/register-upkeep/). ## [Vyper example](https://docs.chain.link/chainlink-automation/guides/compatible-contracts\#vyper-example) You can find a `KeepersConsumer` example [here](https://github.com/smartcontractkit/apeworx-starter-kit/blob/main/contracts/KeepersConsumer.vy). Read the _**apeworx-starter-kit**_ [README](https://github.com/smartcontractkit/apeworx-starter-kit) to learn how to run the example. ## What's next - [\> Access Data Streams Using Automation](https://docs.chain.link/chainlink-automation/guides/streams-lookup) - [\> Register Custom Logic Upkeeps](https://docs.chain.link/chainlink-automation/guides/register-upkeep) - [\> Build Flexible Smart Contracts Using Automation](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps) - [\> Manage your Upkeeps](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps) ## Get the latest Chainlink content straight to your inbox. Email Address [iframe](https://td.doubleclick.net/td/rul/346357746?random=1747983694270&cv=11&fst=1747983694270&fmt=3&bg=ffffff&guid=ON&async=1&gcl_ctr=1>m=45be55l1v891173849z8847174275za200zb847174275&gcd=13l3l3l3l1l1&dma=0&tag_exp=101509157~103116026~103130495~103130497~103200004~103233427~103252644~103252646~103301114~103301116~104481633~104481635&ptag_exp=101509157~103116026~103130498~103130500~103200004~103233427~103252644~103252646~103301114~103301116~104481633~104481635&u_w=1280&u_h=1024&url=https%3A%2F%2Fdocs.chain.link%2Fchainlink-automation%2Fguides%2Fcompatible-contracts&_ng=1&label=Jy3XCMzZ-oYYEPL_k6UB&hn=www.googleadservices.com&frm=0&tiba=Create%20Automation-Compatible%20Contracts%20%7C%20Chainlink%20Documentation&value=0&bttype=purchase&npa=0&pscdl=noapi&auid=909329050.1747983694&uaa=x86&uab=64&uafvl=Chromium%3B136.0.7103.113%7CGoogle%2520Chrome%3B136.0.7103.113%7CNot.A%252FBrand%3B99.0.0.0&uamb=0&uam=&uap=Linux%20x86_64&uapv=6.6.72&uaw=0&ec_mode=a&fledge=1&capi=1&_tu=Cg&em=tv.1&ct_cookie_present=0)[iframe](https://td.doubleclick.net/td/rul/346357746?random=1747983694281&cv=11&fst=1747983694281&fmt=3&bg=ffffff&guid=ON&async=1&gcl_ctr=2>m=45be55l1v891173849z8847174275za200zb847174275&gcd=13l3l3l3l1l1&dma=0&tag_exp=101509157~103116026~103130495~103130497~103200004~103233427~103252644~103252646~103301114~103301116~104481633~104481635&ptag_exp=101509157~103116026~103130498~103130500~103200004~103233427~103252644~103252646~103301114~103301116~104481633~104481635&u_w=1280&u_h=1024&url=https%3A%2F%2Fdocs.chain.link%2Fchainlink-automation%2Fguides%2Fcompatible-contracts&_ng=1&label=_duuCKn_k4cYEPL_k6UB&hn=www.googleadservices.com&frm=0&tiba=Create%20Automation-Compatible%20Contracts%20%7C%20Chainlink%20Documentation&value=0&bttype=purchase&npa=0&pscdl=noapi&auid=909329050.1747983694&uaa=x86&uab=64&uafvl=Chromium%3B136.0.7103.113%7CGoogle%2520Chrome%3B136.0.7103.113%7CNot.A%252FBrand%3B99.0.0.0&uamb=0&uam=&uap=Linux%20x86_64&uapv=6.6.72&uaw=0&ec_mode=a&fledge=1&capi=1&_tu=Cg&em=tv.1&ct_cookie_present=0) ## Flexible Smart Contracts [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Create Flexible, Secure, and Low-Cost Smart Contracts](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps\#overview) In this guide, you will learn how the flexibility of [Chainlink Automation](https://chain.link/automation) enables important design patterns that reduce gas fees, enhance the resilience of dApps, and improve end-user experience. Smart contracts themselves cannot self-trigger their functions at arbitrary times or under arbitrary conditions. Transactions can only be initiated by another account. Start by integrating an example contract to Chainlink Automation that has not yet been optimized. Then, deploy a comparison contract that shows you how to properly use the flexibility of Chainlink Automation to perform complex computations without paying high gas fees. ## [Prerequisites](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps\#prerequisites) This guide assumes you have a basic understanding of [Chainlink Automation](https://chain.link/automation). If you are new to Automation, complete the following guides first: - Learn how to [deploy solidity contracts using Remix and Metamask](https://docs.chain.link/quickstarts/deploy-your-first-contract) - Learn how to make [compatible contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) - [Register Upkeep for a smart contract](https://docs.chain.link/chainlink-automation/guides/register-upkeep) Chainlink Automation is supported on several [networks](https://docs.chain.link/chainlink-automation/overview/supported-networks). ## [Problem: Onchain computation leads to high gas fees](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps\#problem-onchain-computation-leads-to-high-gas-fees) In the guide for [Creating Compatible Contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts), you deployed a basic [counter contract](https://docs.chain.link/chainlink-automation/guides/compatible-contracts/#example-automation-compatible-contract-using-custom-logic-trigger) and verified that the counter increments every 30 seconds. However, more complex use cases can require looping over arrays or performing expensive computation. This leads to expensive gas fees and can increase the premium that end-users have to pay to use your dApp. To illustrate this, deploy an example contract that maintains internal balances. The contract has the following components: - A fixed-size(1000) array `balances` with each element of the array starting with a balance of 1000. - The `withdraw()` function decreases the balance of one or more indexes in the `balances` array. Use this to simulate changes to the balance of each element in the array. - Automation Nodes are responsible for regularly re-balancing the elements using two functions: - The `checkUpkeep()` function checks if the contract requires work to be done. If one array element has a balance of less than `LIMIT`, the function returns `upkeepNeeded == true`. - The `performUpkeep()` function to re-balances the elements. To demonstrate how this computation can cause high gas fees, this example does all of the computation within the transaction. The function finds all of the elements that are less than `LIMIT`, decreases the contract `liquidity`, and increases every found element to equal `LIMIT`. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol"; /** * @dev Example contract which perform all the computation in `performUpkeep` * @notice important to implement {AutomationCompatibleInterface} */ /** * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */ contract BalancerOnChain is AutomationCompatibleInterface { uint256 public constant SIZE = 1000; uint256 public constant LIMIT = 1000; uint256[SIZE] public balances; uint256 public liquidity = 1000000; constructor() { // On the initialization of the contract, all the elements have a balance equal to the limit for (uint256 i = 0; i < SIZE; i++) { balances[i] = LIMIT; } } /// @dev called to increase the liquidity of the contract function addLiquidity(uint256 liq) public { liquidity += liq; } /// @dev withdraw an `amount`from multiple elements of `balances` array. The elements are provided in `indexes` function withdraw(uint256 amount, uint256[] memory indexes) public { for (uint256 i = 0; i < indexes.length; i++) { require(indexes[i] < SIZE, "Provided index out of bound"); balances[indexes[i]] -= amount; } } /// @dev this method is called by the Automation Nodes to check if `performUpkeep` should be performed function checkUpkeep( bytes calldata /* checkData */ ) external view override returns (bool upkeepNeeded, bytes memory performData) { upkeepNeeded = false; for (uint256 i = 0; i < SIZE && !upkeepNeeded; i++) { if (balances[i] < LIMIT) { // if one element has a balance < LIMIT then rebalancing is needed upkeepNeeded = true; } } return (upkeepNeeded, ""); } /// @dev this method is called by the Automation Nodes. it increases all elements which balances are lower than the LIMIT function performUpkeep(bytes calldata /* performData */) external override { uint256 increment; uint256 _balance; for (uint256 i = 0; i < SIZE; i++) { _balance = balances[i]; // best practice: reverify the upkeep is needed if (_balance < LIMIT) { // calculate the increment needed increment = LIMIT - _balance; // decrease the contract liquidity accordingly liquidity -= increment; // rebalance the element balances[i] = LIMIT; } } } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/Automation/BalancerOnChain.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) Test this example using the following steps: 1. Deploy the contract using Remix on the [supported testnet](https://docs.chain.link/chainlink-automation/overview/supported-networks) of your choice. 2. Before registering the upkeep for your contract, decrease the balances of some elements. This simulates a situation where upkeep is required. In Remix, Withdraw 100 at indexes 10,100,300,350,500,600,670,700,900. Pass `100,[10,100,300,350,500,600,670,700,900]` to the withdraw function: ![Withdraw 100 at 10,100,300,350,500,600,670,700,900](https://docs.chain.link/images/automation/balancerOnChain-withdraw.png) You can also perform this step after registering the upkeep if you need to. 3. Register the upkeep for your contract as explained [here](https://docs.chain.link/chainlink-automation/guides/register-upkeep). Because this example has high gas requirements, specify the maximum allowed gas limit of `2,500,000`. 4. After the registration is confirmed, Automation Nodes perform the upkeep. ![BalancerOnChain Upkeep History](https://docs.chain.link/images/automation/balancerOnChain-history.png) 5. Click the transaction hash to see the transaction details in Etherscan. You can find how much gas was used in the upkeep transaction. ![BalancerOnChain Gas](https://docs.chain.link/images/automation/balancerOnChain-gas.png) In this example, the `performUpkeep()` function used **2,481,379** gas. This example has two main issues: - All computation is done in `performUpkeep()`. This is a state modifying function which leads to high gas consumption. - This example is simple, but looping over large arrays with state updates can cause the transaction to hit the gas limit of the [network](https://docs.chain.link/chainlink-automation/overview/supported-networks), which prevents `performUpkeep` from running successfully. To reduce these gas fees and avoid running out of gas, you can make some simple changes to the contract. ## [Solution: Perform complex computations with no gas fees](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps\#solution-perform-complex-computations-with-no-gas-fees) Modify the contract and move the computation to the `checkUpkeep()` function. This computation _doesn’t consume any gas_ and supports multiple upkeeps for the same contract to do the work in parallel. The main difference between this new contract and the previous contract are: - The `checkUpkeep()` function receives [`checkData`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces/#checkdata), which passes arbitrary bytes to the function. Pass a `lowerBound` and an `upperBound` to scope the work to a sub-array of `balances`. This creates several upkeeps with different values of `checkData`. The function loops over the sub-array and looks for the indexes of the elements that require re-balancing and calculates the required `increments`. Then, it returns `upkeepNeeded == true` and `performData`, which is calculated by encoding `indexes` and `increments`. Note that `checkUpkeep()` is a view function, so computation does not consume any gas. - The `performUpkeep()` function takes [performData](https://docs.chain.link/chainlink-automation/reference/automation-interfaces/#performdata) as a parameter and decodes it to fetch the `indexes` and the `increments`. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol"; /** * @dev Example contract which perform most of the computation in `checkUpkeep` * * @notice important to implement {AutomationCompatibleInterface} */ /** * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */ contract BalancerOffChain is AutomationCompatibleInterface { uint256 public constant SIZE = 1000; uint256 public constant LIMIT = 1000; uint256[SIZE] public balances; uint256 public liquidity = 1000000; constructor() { // On the initialization of the contract, all the elements have a balance equal to the limit for (uint256 i = 0; i < SIZE; i++) { balances[i] = LIMIT; } } /// @dev called to increase the liquidity of the contract function addLiquidity(uint256 liq) public { liquidity += liq; } /// @dev withdraw an `amount`from multiple elements of the `balances` array. The elements are provided in `indexes` function withdraw(uint256 amount, uint256[] memory indexes) public { for (uint256 i = 0; i < indexes.length; i++) { require(indexes[i] < SIZE, "Provided index out of bound"); balances[indexes[i]] -= amount; } } /* @dev this method is called by the Chainlink Automation Nodes to check if `performUpkeep` must be done. Note that `checkData` is used to segment the computation to subarrays. * * @dev `checkData` is an encoded binary data and which contains the lower bound and upper bound on which to perform the computation * * @dev return `upkeepNeeded`if rebalancing must be done and `performData` which contains an array of indexes that require rebalancing and their increments. This will be used in `performUpkeep` */ function checkUpkeep( bytes calldata checkData ) external view override returns (bool upkeepNeeded, bytes memory performData) { // perform the computation to a subarray of `balances`. This opens the possibility of having several checkUpkeeps done at the same time (uint256 lowerBound, uint256 upperBound) = abi.decode( checkData, (uint256, uint256) ); require( upperBound < SIZE && lowerBound < upperBound, "Lowerbound and Upperbound not correct" ); // first get number of elements requiring updates uint256 counter; for (uint256 i = 0; i < upperBound - lowerBound + 1; i++) { if (balances[lowerBound + i] < LIMIT) { counter++; } } // initialize array of elements requiring increments as long as the increments uint256[] memory indexes = new uint256[](counter); uint256[] memory increments = new uint256[](counter); upkeepNeeded = false; uint256 indexCounter; for (uint256 i = 0; i < upperBound - lowerBound + 1; i++) { if (balances[lowerBound + i] < LIMIT) { // if one element has a balance < LIMIT then rebalancing is needed upkeepNeeded = true; // store the index which needs increment as long as the increment indexes[indexCounter] = lowerBound + i; increments[indexCounter] = LIMIT - balances[lowerBound + i]; indexCounter++; } } performData = abi.encode(indexes, increments); return (upkeepNeeded, performData); } /* @dev this method is called by the Automation Nodes. it increases all elements whose balances are lower than the LIMIT. Note that the elements are bounded by `lowerBound`and `upperBound` * (provided by `performData` * * @dev `performData` is an encoded binary data which contains the lower bound and upper bound of the subarray on which to perform the computation. * it also contains the increments * * @dev return `upkeepNeeded`if rebalancing must be done and `performData` which contains an array of increments. This will be used in `performUpkeep` */ function performUpkeep(bytes calldata performData) external override { (uint256[] memory indexes, uint256[] memory increments) = abi.decode( performData, (uint256[], uint256[]) ); uint256 _balance; uint256 _liquidity = liquidity; for (uint256 i = 0; i < indexes.length; i++) { _balance = balances[indexes[i]] + increments[i]; _liquidity -= increments[i]; balances[indexes[i]] = _balance; } liquidity = _liquidity; } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/Automation/BalancerOffChain.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) Run this example to compare the gas fees: 1. Deploy the contract using Remix on the [supported testnet](https://docs.chain.link/chainlink-automation/overview/supported-networks) of your choice. 2. Withdraw 100 at 10,100,300,350,500,600,670,700,900. Pass `100,[10,100,300,350,500,600,670,700,900]` to the withdraw function the same way that you did for the [previous example](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps#problem-onchain-computation-leads-to-high-gas-fees). 3. Register three upkeeps for your contract as explained [here](https://docs.chain.link/chainlink-automation/guides/register-upkeep). Because the Automation Nodes handle much of the computation offchain, a gas limit of 200,000 is sufficient. For each registration, pass the following `checkData` values to specify which balance indexes the registration will monitor. **Note**: You must remove any breaking line when copying the values. | Upkeep Name | CheckData(base16) | Remark: calculated using [`abi.encode()`](https://docs.soliditylang.org/en/develop/abi-spec.html#strict-encoding-mode) | | --- | --- | --- | | balancerOffChainSubset1 | 0x000000000000000000000000
00000000000000000000000000
00000000000000000000000000
00000000000000000000000000
0000000000000000000000014c | lowerBound: 0
upperBound: 332 | | balancerOffChainSubset2 | 0x000000000000000000000000
00000000000000000000000000
0000000000014d000000000000
00000000000000000000000000
0000000000000000000000029a | lowerBound: 333
upperBound: 666 | | balancerOffChainSubset3 | 0x000000000000000000000000
00000000000000000000000000
0000000000029b000000000000
00000000000000000000000000
000000000000000000000003e7 | lowerBound: 667
upperBound: 999 | 4. After the registration is confirmed, the three upkeeps run: ![BalancerOffChain1 History](https://docs.chain.link/images/automation/balancerOffChain1-history.png) ![BalancerOffChain2 History](https://docs.chain.link/images/automation/balancerOffChain2-history.png) ![BalancerOffChain3 History](https://docs.chain.link/images/automation/balancerOffChain3-history.png) 5. Click each transaction hash to see the details of each transaction in Etherscan. Find the gas used by each of the upkeep transactions: ![BalancerOffChain1 Gas](https://docs.chain.link/images/automation/balancerOffChain1-gas.png) ![BalancerOffChain2 Gas](https://docs.chain.link/images/automation/balancerOffChain2-gas.png) ![BalancerOffChain3 Gas](https://docs.chain.link/images/automation/balancerOffChain3-gas.png) In this example the total gas used by each `performUpkeep()` function was 133,464 + 133,488 + 133,488 = **400,440**. This is an improvement of about 84% compared to the previous example, which used **2,481,379** gas. ## [Conclusion](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps\#conclusion) Using Chainlink Automation efficiently not only allows you to reduce the gas fees, but also keeps them within predictable limits. That's the reason why [several Defi protocols](https://chainlinktoday.com/prominent-founders-examine-chainlink-keepers-role-in-defis-evolution/) outsource their maintenance tasks to Chainlink Automation. ## What's next - [\> Manage your Upkeeps](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps) - [\> Troubleshoot and Debug Upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Interfaces [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Automation Interfaces](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#overview) Your Automation-compatible contracts may use the following interfaces. You can find them in the [Chainlink repository](https://github.com/smartcontractkit/chainlink/tree/contracts-v1.3.0/contracts/src/v0.8/automation). To understand how to implement these contracts, visit the [Compatible Contracts page](https://docs.chain.link/chainlink-automation/guides/compatible-contracts). - If you want a log event to trigger your upkeep, use the [`ILogAutomation`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#ilogautomation) interface. - If you want to use onchain state (excluding logs) in a custom calculation to trigger your upkeep, use [`AutomationCompatibleInterface`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#automationcompatibleinterface) interface. - If you want to call a function just based on time, consider using a [time-based upkeep](https://docs.chain.link/chainlink-automation/guides/job-scheduler). - If you want to use Automation with Data Streams, use [`StreamsLookupCompatibleInterface`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#streamslookupcompatibleinterface) interface. ## [ILogAutomation](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#ilogautomation) To use log triggers, you will need to implement the [`ILogAutomation.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol) interface in your smart contract. Click on the functions below to understand the parameters: | Function Name | Description | | --- | --- | | `checkLog` | Simulates offchain once a log that matches log specifications is emitted. | | `performUpkeep` | Contains the logic that should be executed onchain when `checkUpkeep` returns `true`. | ### [checkLog function](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#checklog-function) `checkLog` is a `view` function that will be simulated offchain once a log that matches your `LogTriggerConfig` has been emitted. You should parse the log data and the check if something needs to happen onchain. For example, it can trigger the retrieval of Data Streams reports. See the [Data Streams Getting Started](https://docs.chain.link/data-streams/getting-started) guide to see an example. #### [Parameters](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#parameters) | Variable Name | Type | Description | Permissible Values | | --- | --- | --- | --- | | `log` | struct `Log` | Struct log data of the log triggering the upkeep; Automation will form the triggering log into a struct so the user can query log data in `checkLog`. | `struct Log { uint256 index; uint256 timestamp; bytes32 txHash; uint256 blockNumber; bytes32 blockHash; address source; bytes32[] topics; bytes data; }` | | `checkData` | `bytes` | Optional additional bytes the user wants to provide. `checkData` is set at the time of registering the upkeep. | | #### [Example](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#example) ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity ^0.8.7; struct Log { uint256 index; uint256 timestamp; bytes32 txHash; uint256 blockNumber; bytes32 blockHash; address source; bytes32[] topics; bytes data; } function checkLog( Log calldata log, bytes memory checkData ) external returns (bool upkeepNeeded, bytes memory performData); ``` ### [performUpkeep function for log triggers](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#performupkeep-function-for-log-triggers) This function contains the code that will be executed onchain to finalize the trade. #### [Parameters](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#parameters-1) | Variable Name | Type | Description | Permissible Values | | --- | --- | --- | --- | | `performData` | `bytes` | Data encoded in the `checkCallback` function that will be used to execute the function onchain | bytes | ## [AutomationCompatibleInterface](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#automationcompatibleinterface) Custom logic upkeeps need to use the [`AutomationCompatibleInterface.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol) interface. Click on one of the functions below to understand its parameters and limits. | Function Name | Description | | --- | --- | | `checkUpkeep` | Runs offchain to determine if the `performUpkeep` function should be called onchain. | | `performUpkeep` | Contains the logic that should be executed onchain when `checkUpkeep` returns true. | ### [checkUpkeep function](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#checkupkeep-function) This view function contains the logic that runs offchain during every block as an [`eth_call`](https://eth.wiki/json-rpc/API#eth_call) to determine if `performUpkeep` should be executed onchain. To reduce onchain gas usage, attempt to do your gas intensive calculations offchain in `checkUpkeep` and pass the result to `performUpkeep` onchain. It is a best practice to import the [`AutomationCompatible.sol`](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/AutomationCompatible.sol) contract and use the `cannotExecute` modifier to ensure that the method can be used only for simulation purposes. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity function checkUpkeep( bytes calldata checkData ) external view override returns (bool upkeepNeeded, bytes memory performData); ``` Below are the parameters and return values of the `checkUpkeep` function. Click each value to learn more about its design patterns and best practices: **Parameters:** - `checkData`: Fixed and specified at upkeep registration and used in every `checkUpkeep`. Can be empty (0x). **Return Values:** - `upkeepNeeded`: Boolean that when True will trigger the onchain `performUpkeep` call. - `performData`: Bytes that will be used as input parameter when calling `performUpkeep`. If you would like to encode data to decode later, try `abi.encode`. #### [checkData](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#checkdata) You can pass information into your `checkUpkeep` function from your [upkeep registration](https://docs.chain.link/chainlink-automation/guides/register-upkeep) to execute different code paths. For example, to check the balance on a specific address, set the `checkData` to abi encode the address. To learn how to create flexible upkeeps with checkData, please see our [flexible upkeeps](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps) page. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity function checkUpkeep( bytes calldata checkData ) public view returns (bool, bytes memory) { address wallet = abi.decode(checkData, (address)); return (wallet.balance < 1 ether, bytes("")); } ``` Tips on using `checkData`: - **Managing unbounded upkeeps**: Limit the problem set of your onchain execution by creating a range bound for your upkeep to check and perform. This allows you to keep within predefined gas limits, which creates a predictable upper bound gas cost on your transactions. Break apart your problem into multiple upkeep registrations to limit the scope of work. **Example**: You could create an upkeep for each subset of addresses that you want to service. The ranges could be 0 to 49, 50 to 99, and 100 to 149. - **Managing code paths**: Pass in data to your `checkUpkeep` to make your contract logic go down different code paths. This can be used in creative ways based on your use case needs. **Example**: You could support multiple types of upkeep within a single contract and pass a function selector through the `checkData` function. #### [performData](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#performdata) The response from `checkUpkeep` is passed to the `performUpkeep` function as `performData`. This allows you to perform complex and gas intensive calculations as a simulation offchain and only pass the needed data onchain. You can create a highly flexible offchain computation infrastructure that can perform precise actions onchain by using `checkData` and `performData`. Both of these computations are entirely programmable. ### [performUpkeep function for custom logic triggers](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#performupkeep-function-for-custom-logic-triggers) When `checkUpkeep` returns `upkeepNeeded == true`, the Automation node broadcasts a transaction to the blockchain to execute your `performUpkeep` function onchain with `performData` as an input. Ensure that your `performUpkeep` is _idempotent_. Your `performUpkeep` function should change state such that `checkUpkeep` will not return `true` for the same subset of work once said work is complete. Otherwise the Upkeep will remain eligible and result in multiple performances by the Chainlink Automation Network on the exactly same subset of work. As a best practice, always check conditions for your upkeep at the start of your `performUpkeep` function. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity function performUpkeep(bytes calldata performData) external override; ``` **Parameters:** - `performData`: Data which was passed back from the `checkData` simulation. If it is encoded, it can easily be decoded into other types by calling `abi.decode`. This data should always be validated against the contract's current state. #### [performData](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#performdata-1) You can perform complex and broad offchain computation, then execute onchain state changes on a subset that meets your conditions. This can be done by passing the appropriate inputs within `performData` based on the results from your `checkUpkeep`. This pattern can greatly reduce your onchain gas usage by narrowing the scope of work intelligently in your own Solidity code. - **Identify a list of addresses that require work**: You might have a number of addresses that you are validating for conditions before your contract takes an action. Doing this onchain can be expensive. Filter the list of addresses by validating the necessary conditions within your `checkUpkeep` function. Then, pass the addresses that meet the condition through the `performData` function. For example, if you have a "top up" contract that ensures several hundred account balances never decrease below a threshold, pass the list of accounts that meet the conditions so that the `performUpkeep` function validates and tops up only a small subset of the accounts. - **Identify the subset of states that must be updated**: If your contract maintains complicated objects such as arrays and structs, or stores a lot of data, you should read through your storage objects within your `checkUpkeep` and run your proprietary logic to determine if they require updates or maintenance. After that is complete, you can pass the known list of objects that require updates through the `performData` function. ## [StreamsLookupCompatibleInterface](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#streamslookupcompatibleinterface) See the [Data Streams Getting Started](https://docs.chain.link/data-streams/getting-started) guide to see an example of how to use this interface. To use Data Streams with Automation, your contract must be [Automation-compatible](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) and include a `StreamsLookupCompatibleInterface` interface. This interface fetches and processes Data Streams reports. | Function Name | Description | | --- | --- | | `StreamsLookup` revert | Triggers the retrieval of specified reports. | | `checkCallback` | Simulates offchain to receive signed reports and conduct final parsing before sending data onchain via `performUpkeep`. | ### [StreamsLookup revert](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#streamslookup-revert) Automation network will use this revert to trigger fetching of the specified reports. #### [Parameters](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#parameters) | Variable Name | Type | Description | Permissible Values | | --- | --- | --- | --- | | `feedParamKey` | `String` | Specify the feed identifiers that will be provided | `"feedIDs"` | | `feeds` | `String[]` | String list of feed identifiers | e.g. `["feedID1","feedID2",..]` with a maximum of 5 IDs | | `timeParamKey` | `String` | Specify query type | `"timestamp"` | | `time` | `uint256` | Specify query value | e.g. `log.timestamp` from the returned Log struct in `checkLog` | | `extraData` | `bytes` | Any extra data user wants to receive in callback, alongside API bytes\[\] | e.g. log data | #### [Outputs](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#outputs) The fetched signed reports will be provided to the user in [`checkCallback`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#checkcallback-function). This is an offchain compute that the user can use to structure data as needed before performing the onchain confirmation and validation transactions. | Variable Name | Type | Description | Usage examples | | --- | --- | --- | --- | | `values` | `bytes[]` | List of signed reports fetched from API | List of signed reports `values[0], values[1]…` ordered to match order in feeds | | `extraData` | `bytes` | Bytes data sent as part of original StreamsLookup revert error | e.g. can send log data if you want to decode to use | ### [checkCallback function](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#checkcallback-function) This is a `view` function that will be simulated offchain to receive the signed reports and conduct any final parsing before sending data onchain via `performUpkeep`. Inputs will match outputs of the `oracleLookup` revert. #### [Parameters](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#parameters-1) | Variable Name | Type | Description | Permissible Values | | --- | --- | --- | --- | | `values` | `bytes[]` | List of signed reports fetched from Data Streams API | List of signed reports `values[0], values[1]…` ordered to match order in feeds | | `extraData` | `bytes` | Bytes data sent as part of original StreamsLookup revert error | e.g. can send log data if you want to decode to use | #### [Outputs](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#outputs-1) | Variable Name | Type | Description | Permissible Values | | --- | --- | --- | --- | | `performData` | `bytes` | Encoded data that will be used to execute the `performUpkeep` function onchain. E.g. encoded signed reports and log data if need be | `bytes` | | `upkeepNeeded` | `boolean` | When set to `true`, this will trigger the `performUpkeep` function to be executed onchain | `true` of `false` | ### [checkErrorHandler function](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#checkerrorhandler-function) This function is simulated offchain to respond to [error codes](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler#error-codes) before sending data onchain in the `performUpkeep` function. #### [Parameters](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#parameters-2) | Variable Name | Type | Description | Permissible Values | | --- | --- | --- | --- | | `errorCode` | `uint` | Error code returned by StreamsLookup error handler | Example: "808400" ( `ErrCodeStreamsBadRequest`) | | `extraData` | `bytes` | Bytes data sent as part of the original `MercuryLookup` revert error | This field can send log data if you want to decode it and use it. | #### [Outputs](https://docs.chain.link/chainlink-automation/reference/automation-interfaces\#outputs-2) | Variable Name | Type | Description | Permissible Values | | --- | --- | --- | --- | | `performData` | `bytes` | Data encoded in the `oracleCallback` function that will be used to execute function onchain, e.g. encode signed reports and log data if need be | `bytes` | | `upkeepNeeded` | `boolean` | When set to `true`, this triggers the `performUpkeep` function to be executed onchain | `true` or `false` | ## What's next - [\> Automation Contracts](https://docs.chain.link/chainlink-automation/reference/automation-contracts) - [\> Troubleshoot and Debug Upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors) ## Get the latest Chainlink content straight to your inbox. Email Address ## Data Streams Automation Guide [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Access Data Streams Using Automation](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#overview) This guide shows you how to read data from a Data Streams stream, verify the answer onchain, and store it. This example uses the _[Streams Trade](https://docs.chain.link/data-streams/streams-trade) implementation_, with a [Chainlink Automation Log Trigger](https://docs.chain.link/chainlink-automation/guides/log-trigger) to check for events that require data. For this example, the log trigger comes from a simple emitter contract. Chainlink Automation then uses `StreamsLookup` to retrieve a signed report from the Data Streams Aggregation Network, return the data in a callback, and run the [`performUpkeep` function](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#performupkeep-function-for-log-triggers) on your registered upkeep contract. The `performUpkeep` function calls the `verify` function on the verifier contract. Note: To learn how to use the _[Streams Direct](https://docs.chain.link/data-streams/streams-direct) implementation_ of Data Streams, see the [Fetch and decode reports via a REST API](https://docs.chain.link/data-streams/tutorials/streams-direct/streams-direct-api) guide or the [Stream and decode reports via WebSocket](https://docs.chain.link/data-streams/tutorials/streams-direct/streams-direct-ws) guide. ## [Before you begin](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#before-you-begin) - [Contact\\ us](https://chainlinkcommunity.typeform.com/datastreams?typeform-source=docs.chain.link#ref_id=docs) to request mainnet or testnet access. - If you are new to smart contract development, learn how to [Deploy Your First Smart Contract](https://docs.chain.link/quickstarts/deploy-your-first-contract) so you are familiar with the tools that are necessary for this guide: - The [Solidity](https://soliditylang.org/) programming language - The [MetaMask](https://metamask.io/) wallet - The [Remix](https://remix.ethereum.org/) development environment - This guide requires testnet ETH and LINK on _Arbitrum Sepolia_. Both are available at [faucets.chain.link](https://faucets.chain.link/arbitrum-sepolia). - Learn how to [Fund your contract with LINK](https://docs.chain.link/resources/fund-your-contract). ## [Tutorial](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#tutorial) ### [Deploy the Chainlink Automation upkeep contract](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#deploy-the-chainlink-automation-upkeep-contract) Deploy an upkeep contract that is enabled to retrieve data from Data Streams. For this example, you will read from the ETH/USD stream on Arbitrum Sepolia. This stream ID is `0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg). See the [Data Streams Crypto streams](https://docs.chain.link/data-streams/crypto-streams) page for a complete list of available crypto assets. 1. [Open the StreamsUpkeep.sol](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/StreamsUpkeep.sol) contract in Remix. [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/StreamsUpkeep.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) 2. Select the `StreamsUpkeep.sol` contract in the **Solidity Compiler** tab. ![Chainlink Data Streams Solidity Compiler](https://docs.chain.link/images/data-streams/getting-started/solidity-compiler.webp) 3. Compile the contract. You can ignore the warning messages for this example. 4. Open MetaMask and set the network to _Arbitrum Sepolia_. If you need to add Arbitrum Sepolia to your wallet, you can find the chain ID and the LINK token contract address on the [LINK Token Contracts](https://docs.chain.link/resources/link-token-contracts#arbitrum-sepolia-testnet) page. - [Arbitrum Sepolia testnet and LINK token contract](https://docs.chain.link/resources/link-token-contracts#arbitrum-sepolia-testnet) 5. On the **Deploy & Run Transactions** tab in Remix, select _Injected Provider - MetaMask_ in the **Environment** list. Remix will use the MetaMask wallet to communicate with _Arbitrum Sepolia_. ![Chainlink Data Streams Injected Provider MetaMask](https://docs.chain.link/images/data-streams/getting-started/injected-provider.webp) 6. In the **Contract** section, select the `StreamsUpkeep` contract and fill in the Arbitrum Sepolia **verifier proxy address**: `0x2ff010DEbC1297f19579B4246cad07bd24F2488A`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg). You can find the verifier proxy addresses on the [Stream Addresses](https://docs.chain.link/data-streams/crypto-streams) page. ![Chainlink Data Streams Remix Deploy Upkeep Contract](https://docs.chain.link/images/data-streams/getting-started/deploy-upkeep.webp) 7. Click the **Deploy** button to deploy the contract. MetaMask prompts you to confirm the transaction. Check the transaction details to ensure you deploy the contract to _Arbitrum Sepolia_. 8. After you confirm the transaction, the contract address appears under the **Deployed Contracts** list in Remix. Save this contract address for later. ![Chainlink Data Streams Remix Deployed Upkeep Contract](https://docs.chain.link/images/data-streams/getting-started/deployed-upkeep-1.webp) ### [Deploy the emitter contract](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#deploy-the-emitter-contract) This contract emits logs that trigger the upkeep. This code can be part of your dApp. For example, you might emit log triggers when your users initiate a trade or other action requiring data retrieval. For this Getting Started guide, use a very simple emitter so you can test the upkeep and data retrieval. 1. [Open the LogEmitter.sol](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/LogEmitter.sol) contract in Remix. [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/LogEmitter.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) 2. Under the **Solidity Compiler** tab, select the `0.8.19` Solidity compiler and click the **Compile LogEmitter.sol** button to compile the contract. ![Chainlink Data Streams Remix Compile Log Emitter Contract](https://docs.chain.link/images/data-streams/getting-started/compile-logemitter.webp) 3. Open MetaMask and make sure the network is still set to _Arbitrum Sepolia_. 4. On the **Deploy & Run Transactions** tab in Remix, ensure the **Environment** is still set to _Injected Provider - MetaMask_. ![Chainlink Data Streams Injected Provider MetaMask](https://docs.chain.link/images/data-streams/getting-started/injected-provider.webp) 5. Click the **Deploy** button to deploy the contract. MetaMask prompts you to confirm the transaction. Check the transaction details to ensure you deploy the contract to _Arbitrum Sepolia_. ![Chainlink Data Streams Deploy Emitter Contract](https://docs.chain.link/images/data-streams/getting-started/deploy-logemitter.webp) 6. After you confirm the transaction, the contract address appears in the **Deployed Contracts** list. Save this contract address for later. ![Chainlink Data Streams Deployed Emitter Contract](https://docs.chain.link/images/data-streams/getting-started/deployed-logemitter.webp) ### [Register the upkeep](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#register-the-upkeep) Register a new **Log trigger** upkeep. See [Automation Log Triggers](https://docs.chain.link/chainlink-automation/guides/log-trigger) to learn more about how to register Log Trigger upkeeps. 01. Go to the [Chainlink Automation UI](https://automation.chain.link/arbitrum-sepolia) for _Arbitrum Sepolia_ and connect your browser wallet. 02. Click **Register new Upkeep**. 03. Select the **Log trigger** upkeep type and click **Next**. 04. Specify the upkeep contract address you saved earlier as the **Contract to automate**. In this example, you can ignore the warning about the Automation compatible contract verification. Click **Next**. 05. Specify the emitter contract address that you saved earlier. This tells Chainlink Automation what contracts to watch for log triggers. Then click **Next**. 06. Provide the ABI if the contract is not validated. To find the ABI of your contract in Remix, navigate to the **Solidity Compiler** tab. Then, copy the ABI to your clipboard using the button at the bottom of the panel. ![Chainlink Data Streams Remix Log Emitter ABI](https://docs.chain.link/images/data-streams/getting-started/remix-copy-abi.webp) 07. Select the `Log` event as the triggering event in the **Emitted log** dropdown. **Log index topic filters** are optional filters to narrow the logs you want to trigger your upkeep. For this example, leave the field blank. Click **Next**. 08. Specify a name for the upkeep. 09. Specify a **Starting balance** of 1 testnet LINK for this example. You can retrieve unused LINK later. 10. Leave the **Check data** value and other fields blank for now, and click **Register Upkeep**. MetaMask prompts you to confirm the transaction. Wait for the transaction to complete. ### [Fund the upkeep contract](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#fund-the-upkeep-contract) In this example, the upkeep contract pays for onchain verification of reports from Data Streams. The Automation subscription does not cover the cost. Open MetaMask and send 1 testnet LINK on _Arbitrum Sepolia_ to the upkeep contract address you saved earlier. ![Chainlink Data Streams Fund Deployed Upkeep](https://docs.chain.link/images/data-streams/getting-started/fund-deployed-upkeep.webp) ### [Emit a log](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#emit-a-log) You can use your emitter contract to emit a log and initiate the upkeep, which retrieves data for the specified stream ID. 1. In Remix, on the **Deploy & Run Transactions** tab, expand your emitter contract under the **Deployed Contracts** section. ![Chainlink Data Streams Emit Log](https://docs.chain.link/images/data-streams/getting-started/emitter-emitlog.webp) 2. Click the `emitLog` button to call the function and emit a log. MetaMask prompts you to accept the transaction. After the transaction is complete, the log is emitted, and the upkeep is triggered. You can find the upkeep transaction hash in the [Chainlink Automation UI](https://automation.chain.link/arbitrum-sepolia). Check to make sure the transaction is successful. ![](https://docs.chain.link/images/data-streams/getting-started/request-fulfilled.webp) ### [View the retrieved price](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#view-the-retrieved-price) The retrieved price is stored in the `lastDecodedPrice` storage variable. 1. On the **Deploy & Run Transactions** tab in Remix, expand the details of your upkeep contract in the **Deployed Contracts** section. 2. Click the `lastDecodedPrice` getter function to view the retrieved price. The answer on the ETH/USD stream uses 18 decimal places, so an answer of `248412100000000000` indicates an ETH/USD price of 2,484.121. Some streams may use a different number of decimal places for answers. See the [Data Streams Crypto streams](https://docs.chain.link/data-streams/crypto-streams) page for more information. ![Chainlink Data Streams Deployed Upkeep](https://docs.chain.link/images/data-streams/getting-started/deployed-upkeep-2.webp) ## [Examine the code](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#examine-the-code) The example code you deployed has all the interfaces and functions required to work with Chainlink Automation as an upkeep contract. It follows a similar flow to the trading flow in the [Architecture](https://docs.chain.link/data-streams/architecture#example-trading-flow-using-streams-trade) documentation but uses a basic log emitter to simulate the client contract that would initiate a `StreamsLookup`. After the contract receives and verifies the report, `performUpkeep` stores the price from the report in the `lastDecodedPrice` variable. You could modify this to use the data in a way that works for your specific use case and application. The code example uses `revert` with `StreamsLookup` to convey call information about what streams to retrieve. See the [EIP-3668 rationale](https://eips.ethereum.org/EIPS/eip-3668#rationale) for more information about how to use `revert` in this way. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol"; import {StreamsLookupCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol"; import {ILogAutomation, Log} from "@chainlink/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol"; import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol"; import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol"; import {IERC20} from "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; /** * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE FOR DEMONSTRATION PURPOSES. * DO NOT USE THIS CODE IN PRODUCTION. */ // Custom interfaces for IVerifierProxy and IFeeManager interface IVerifierProxy { /** * @notice Verifies that the data encoded has been signed. * correctly by routing to the correct verifier, and bills the user if applicable. * @param payload The encoded data to be verified, including the signed * report. * @param parameterPayload Fee metadata for billing. For the current implementation this is just the abi-encoded fee token ERC-20 address. * @return verifierResponse The encoded report from the verifier. */ function verify( bytes calldata payload, bytes calldata parameterPayload ) external payable returns (bytes memory verifierResponse); function s_feeManager() external view returns (IVerifierFeeManager); } interface IFeeManager { /** * @notice Calculates the fee and reward associated with verifying a report, including discounts for subscribers. * This function assesses the fee and reward for report verification, applying a discount for recognized subscriber addresses. * @param subscriber The address attempting to verify the report. A discount is applied if this address * is recognized as a subscriber. * @param unverifiedReport The report data awaiting verification. The content of this report is used to * determine the base fee and reward, before considering subscriber discounts. * @param quoteAddress The payment token address used for quoting fees and rewards. * @return fee The fee assessed for verifying the report, with subscriber discounts applied where applicable. * @return reward The reward allocated to the caller for successfully verifying the report. * @return totalDiscount The total discount amount deducted from the fee for subscribers */ function getFeeAndReward( address subscriber, bytes memory unverifiedReport, address quoteAddress ) external returns (Common.Asset memory, Common.Asset memory, uint256); function i_linkAddress() external view returns (address); function i_nativeAddress() external view returns (address); function i_rewardManager() external view returns (address); } contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { error InvalidReportVersion(uint16 version); // Thrown when an unsupported report version is provided to verifyReport. /** * @dev Represents a data report from a Data Streams stream for v3 schema (crypto streams). * The `price`, `bid`, and `ask` values are carried to either 8 or 18 decimal places, depending on the stream. * For more information, see https://docs.chain.link/data-streams/crypto-streams and https://docs.chain.link/data-streams/reference/report-schema */ struct ReportV3 { bytes32 feedId; // The stream ID the report has data for. uint32 validFromTimestamp; // Earliest timestamp for which price is applicable. uint32 observationsTimestamp; // Latest timestamp for which price is applicable. uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH). uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median price (8 or 18 decimals). int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals). int192 ask; // Simulated price impact of a sell order up to the X% depth of liquidity utilisation (8 or 18 decimals). } /** * @dev Represents a data report from a Data Streams stream for v4 schema (RWA streams). * The `price` value is carried to either 8 or 18 decimal places, depending on the stream. * The `marketStatus` indicates whether the market is currently open. Possible values: `0` (`Unknown`), `1` (`Closed`), `2` (`Open`). * For more information, see https://docs.chain.link/data-streams/rwa-streams and https://docs.chain.link/data-streams/reference/report-schema-v4 */ struct ReportV4 { bytes32 feedId; // The stream ID the report has data for. uint32 validFromTimestamp; // Earliest timestamp for which price is applicable. uint32 observationsTimestamp; // Latest timestamp for which price is applicable. uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH). uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median benchmark price (8 or 18 decimals). uint32 marketStatus; // The DON's consensus on whether the market is currently open. } struct Quote { address quoteAddress; } IVerifierProxy public verifier; address public FEE_ADDRESS; string public constant DATASTREAMS_FEEDLABEL = "feedIDs"; string public constant DATASTREAMS_QUERYLABEL = "timestamp"; int192 public lastDecodedPrice; // This example reads the ID for the ETH/USD report. // Find a complete list of IDs at https://docs.chain.link/data-streams/crypto-streams. string[] public feedIds = [\ "0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782"\ ]; constructor(address _verifier) { verifier = IVerifierProxy(_verifier); } // This function uses revert to convey call information. // See https://eips.ethereum.org/EIPS/eip-3668#rationale for details. function checkLog( Log calldata log, bytes memory ) external returns (bool upkeepNeeded, bytes memory performData) { revert StreamsLookup( DATASTREAMS_FEEDLABEL, feedIds, DATASTREAMS_QUERYLABEL, log.timestamp, "" ); } /** * @notice this is a new, optional function in streams lookup. It is meant to surface streams lookup errors. * @return upkeepNeeded boolean to indicate whether the keeper should call performUpkeep or not. * @return performData bytes that the keeper should call performUpkeep with, if * upkeep is needed. If you would like to encode data to decode later, try `abi.encode`. */ function checkErrorHandler( uint256 /*errCode*/, bytes memory /*extraData*/ ) external pure returns (bool upkeepNeeded, bytes memory performData) { return (true, "0"); // Hardcoded to always perform upkeep. // Read the StreamsLookup error handler guide for more information. // https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler } // The Data Streams report bytes is passed here. // extraData is context data from stream lookup process. // Your contract may include logic to further process this data. // This method is intended only to be simulated offchain by Automation. // The data returned will then be passed by Automation into performUpkeep function checkCallback( bytes[] calldata values, bytes calldata extraData ) external pure returns (bool, bytes memory) { return (true, abi.encode(values, extraData)); } // function will be performed onchain function performUpkeep(bytes calldata performData) external { // Decode the performData bytes passed in by CL Automation. // This contains the data returned by your implementation in checkCallback(). (bytes[] memory signedReports, bytes memory extraData) = abi.decode( performData, (bytes[], bytes) ); bytes memory unverifiedReport = signedReports[0]; (, /* bytes32[3] reportContextData */ bytes memory reportData) = abi .decode(unverifiedReport, (bytes32[3], bytes)); // Extract report version from reportData uint16 reportVersion = (uint16(uint8(reportData[0])) << 8) | uint16(uint8(reportData[1])); // Validate report version if (reportVersion != 3 && reportVersion != 4) { revert InvalidReportVersion(uint8(reportVersion)); } // Report verification fees IFeeManager feeManager = IFeeManager(address(verifier.s_feeManager())); IRewardManager rewardManager = IRewardManager( address(feeManager.i_rewardManager()) ); address feeTokenAddress = feeManager.i_linkAddress(); (Common.Asset memory fee, , ) = feeManager.getFeeAndReward( address(this), reportData, feeTokenAddress ); // Approve rewardManager to spend this contract's balance in fees IERC20(feeTokenAddress).approve(address(rewardManager), fee.amount); // Verify the report bytes memory verifiedReportData = verifier.verify( unverifiedReport, abi.encode(feeTokenAddress) ); // Decode verified report data into the appropriate Report struct based on reportVersion if (reportVersion == 3) { // v3 report schema ReportV3 memory verifiedReport = abi.decode( verifiedReportData, (ReportV3) ); // Store the price from the report lastDecodedPrice = verifiedReport.price; } else if (reportVersion == 4) { // v4 report schema ReportV4 memory verifiedReport = abi.decode( verifiedReportData, (ReportV4) ); // Store the price from the report lastDecodedPrice = verifiedReport.price; } } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/StreamsUpkeep.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) ### [Initializing the upkeep contract](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#initializing-the-upkeep-contract) When you deploy the contract, you define the verifier proxy address. You can find this address on the [Stream Addresses](https://docs.chain.link/data-streams/crypto-streams) page. The `IVerifierProxy` interface provides the following functions: - The `s_feeManager` function to estimate the verification fees. - The `verify` function to verify the report onchain. ### [Emitting a log, retrieving, and verifying the report](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#emitting-a-log-retrieving-and-verifying-the-report) After registering your upkeep contract with Chainlink Automation with a log trigger, you can emit a log with the `emitLog` function from your emitter contract. 1. The emitted log triggers the Chainlink Automation upkeep. 2. Chainlink Automation then uses `StreamsLookup` to retrieve a signed report from the Data Streams Aggregation Network, returns the data in a callback ( `checkCallback`), and runs the `performUpkeep` function on your registered upkeep contract. 3. The `performUpkeep` function calls the `verify` function on the verifier contract to verify the report onchain. 4. In this example, the `performUpkeep` function also stores the price from the report in the `lastDecodedPrice` state variable. ### [Viewing the retrieved price](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#viewing-the-retrieved-price) The `lastDecodedPrice` getter function of your upkeep contract retrieves the last price stored by the `performUpkeep` function in the `lastDecodedPrice` state variable of the `StreamsUpkeep` contract. ### [Feed ID types and conversion](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#feed-id-types-and-conversion) Chainlink Data Streams uses different data types for feed IDs at different stages of the process: - The [`StreamsLookup` error](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol#L6) requires feed IDs to be provided as an array of `string`, - The decoded reports within the contract use `bytes32` types for feed IDs (see the [Report Schemas](https://docs.chain.link/data-streams/reference/report-schema) reference). If your application needs to compare the feed ID(s) sent in the `StreamsLookup` with those received in the report(s), you must convert between `string` and `bytes32` types. ### [Optional: Handle Data Streams fetching errors offchain with `checkErrorHandler`](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#optional-handle-data-streams-fetching-errors-offchain-with-checkerrorhandler) When Automation detects the triggering event, it runs the `checkLog` function of your upkeep contract, which includes a `StreamsLookup` revert custom error. The `StreamsLookup` revert enables your upkeep to fetch a report from the Data Streams Aggregation Network. If the report is fetched successfully, the `checkCallback` function is evaluated offchain. Otherwise, the `checkErrorHandler` function is evaluated offchain to determine what Automation should do next. In this example, the `checkErrorHandler` is set to always return `true` for `upkeepNeeded`. This implies that the upkeep is always triggered, even if the report fetching fails. You can modify the `checkErrorHandler` function to handle errors offchain in a way that works for your specific use case. Read more about [using the StreamsLookup error handler](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler). ## [Debugging StreamsLookup](https://docs.chain.link/chainlink-automation/guides/streams-lookup\#debugging-streamslookup) Read our [debugging section](https://docs.chain.link/chainlink-automation/reference/debugging-errors) to learn how to identify and resolve common errors when using `StreamsLookup`. ## What's next - [\> Troubleshoot and Debug Upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors) - [\> Automation Interfaces](https://docs.chain.link/chainlink-automation/reference/automation-interfaces) - [\> Automation Contracts](https://docs.chain.link/chainlink-automation/reference/automation-contracts) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Overview [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture\#overview) The following diagram describes the architecture of the Chainlink Automation Network. The Chainlink Automation Registry governs the actors on the network and compensates Automation Nodes for performing successful upkeeps. Developers can register their Upkeeps, and Node Operators can register as Automation Nodes. ![](https://docs.chain.link/images/automation/automation_2_architecture.png) ## [How it works](https://docs.chain.link/chainlink-automation/concepts/automation-architecture\#how-it-works) Automation Nodes form a peer-to-peer network using Chainlink's OCR3 protocol. Nodes use the `Registry` to know which Upkeeps to service and **simulate** `checkUpkeep` functions on their own block nodes to determine when upkeeps are eligible to be performed. Using Chainlink's OCR3 protocol, nodes then get consensus on which upkeeps to perform and sign a report. The signed report contains the performData that will be executed onchain and the report is validated on the `Registry` before execution to provide cryptographic guarantees. The network has built-in redundancy and will still perform your upkeep even if some nodes are down. ![](https://docs.chain.link/images/automation/automation_2_diagram_v3.png) Chainlink Automation use the same battle-tested transaction manager mechanism built and used by Chainlink Data Feeds. This creates a hyper-reliable automation service that can execute and confirm transactions even during intense gas spikes or on chains with significant reorgs. ## [Internal monitoring](https://docs.chain.link/chainlink-automation/concepts/automation-architecture\#internal-monitoring) Internally, Chainlink Automation also uses its own monitoring and alerting mechanisms to maintain a healthy network and ensure developers get the industry leading reliability and performance. ## [Supported networks and costs](https://docs.chain.link/chainlink-automation/concepts/automation-architecture\#supported-networks-and-costs) For a list of blockchains that is supported by Chainlink Automation, please review the [supported networks page](https://docs.chain.link/chainlink-automation/overview/supported-networks). To learn more about the cost of using Chainlink Automation, please review the [Automation economics](https://docs.chain.link/chainlink-automation/overview/automation-economics) page. ## What's next - [\> Automation Contracts](https://docs.chain.link/chainlink-automation/reference/automation-contracts) - [\> Automation Interfaces](https://docs.chain.link/chainlink-automation/reference/automation-interfaces) - [\> Automation Best Practices](https://docs.chain.link/chainlink-automation/concepts/best-practice) ## Get the latest Chainlink content straight to your inbox. Email Address ## Counting dNFT Tutorial [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) On this page # [Counting dNFT](https://docs.chain.link/chainlink-automation/tutorials/counting-dnft\#overview) View the template [here](https://github.com/smartcontractkit/smart-contract-examples/tree/main/counting-svg). This repository houses an example that automates counting with a dynamic SVG. The main contract is `counting-svg.sol` which is automated to create an entirely onchain SVG. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT // An example of a consumer contract that relies on a subscription for funding. pragma solidity 0.8.17; // Imports import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/Base64.sol"; contract CountSVG is ERC721, ERC721URIStorage, Ownable { // Setup counters, we use the OpenZeppelin Counters library using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; // Initialize the counter to 0 uint256 count = 0; // Constructor for the contract constructor() ERC721("Counting SVG", "cSVG") {} // Mint a new NFT and update the URI function safeMint(address to) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); updateURI(); } // Update the URI for the NFT function updateURI() internal { // Build the SVG string memory finalSVG = buildSVG(); //Encode the SVG and add the metadata string memory json = Base64.encode( bytes( string( abi.encodePacked( '{"name": "Counting SVG",', '"description": "An Automated Counting SVG",', '"image": "data:image/svg+xml;base64,', Base64.encode(bytes(finalSVG)), '"}' ) ) ) ); // Set the URI string string memory finalTokenURI = string(abi.encodePacked("data:application/json;base64,", json)); // Update the URI // NOTE: This is hardcoded to the first SVG only _setTokenURI(0, finalTokenURI); } // Create the SVG function buildSVG() internal view returns (string memory) { string memory headSVG = " "; string memory tailSVG = ""; string memory bodySVG = string( abi.encodePacked( "", Strings.toString(count), "" ) ); // Concatenate the SVG parts string memory _finalSVG = string(abi.encodePacked(headSVG, bodySVG, tailSVG)); return _finalSVG; } // Increment the counter function addToCount() public { count = count + 1; updateURI(); } function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { super._burn(tokenId); } function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { return super.tokenURI(tokenId); } } ``` ## What's next - [\> Automate the Batch Reveal of Collections](https://docs.chain.link/quickstarts/batch-reveal) - [\> Create Dynamic NFTs](https://docs.chain.link/quickstarts/dynamic-metadata) - [\> Automate Top-Up for Contract Balances](https://docs.chain.link/quickstarts/eth-balance-monitor) - [\> Automation Top-Up for VRF Subscriptions](https://docs.chain.link/quickstarts/vrf-subscription-monitor) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Limits [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Chainlink Automation Service Limits](https://docs.chain.link/chainlink-automation/overview/service-limits\#overview) ## [Maximum logs processed for log trigger upkeeps](https://docs.chain.link/chainlink-automation/overview/service-limits\#maximum-logs-processed-for-log-trigger-upkeeps) Chainlink Automation nodes look back over a limited range of the latest blocks on any particular chain. During this process, the nodes process a limited number of logs per block per upkeep, using a minimum dequeue method to ensure that the latest logs are processed first. After this, the nodes may process additional remaining logs on a best effort basis, but this is not guaranteed. If you need all the remaining logs to be processed, configure a manual trigger as backup. Expect the following numbers of logs to be processed: | Chain | Logs per block per upkeep | | --- | --- | | Ethereum | 20 | | BSC (BNB) | 4 | | Polygon | 4 | | Avalanche | 4 | | Gnosis | 1 | | OP | 4 | | BASE | 4 | | Arbitrum | 1 log every 2 blocks, or 2 logs per second | Note: Log triggers are not supported on Fantom. ## What's next - [\> Register Log Trigger Upkeeps](https://docs.chain.link/chainlink-automation/guides/log-trigger/) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Guide [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Getting Started with Chainlink Automation](https://docs.chain.link/chainlink-automation/overview/getting-started\#overview) Chainlink Automation will reliably execute smart contract functions using a variety of triggers. Explore the examples below to see how Chainlink Automation works for each type of trigger. Before you begin, you will need an active cryptocurrency wallet such as Metamask. - **Time-based trigger**: Use a time-based trigger to execute your function according to a time schedule. - **Custom logic trigger**: Use a custom logic trigger to provide custom solidity logic that Automation Nodes evaluate (offchain) to determine when to execute your function onchain. - **Log trigger**: Use log data as both trigger and input. ## [Try out Chainlink Automation](https://docs.chain.link/chainlink-automation/overview/getting-started\#try-out-chainlink-automation) Click the tabs below to use Chainlink Automation with each type of trigger: Time-basedCustom LogicLog Trigger **Increment a counter every 5 minutes using our example contract.** 01. Navigate to the [Chainlink Automation app](https://automation.chain.link/) and connect to **Arbitrum Sepolia** in the top dropdown menu. 02. Connect your cryptocurrency wallet to the app if you haven't done so already. You may also need to fetch Arbitrum Sepolia testnet LINK [here](https://faucets.chain.link/arbitrum-sepolia). 03. Click **Register new Upkeep** and select **Time-based** trigger. 04. Under _Target contract address_, enter [0x083935210524c0A8922ec610d1063Aa0A54d9d70](https://sepolia.arbiscan.io/address/0x083935210524c0A8922ec610d1063Aa0A54d9d70 "0x083935210524c0A8922ec610d1063Aa0A54d9d70")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg). This is a simple counter contract that increments with each call. View the source code [here](https://sepolia.arbiscan.io/address/0x083935210524c0A8922ec610d1063Aa0A54d9d70#code). 05. In the _Contract call_ section, enter `addInteger` under _Target function_. In the _Function inputs_ section, enter a number to increment by under _intToAdd_. Then click **Next**. ![](https://docs.chain.link/images/automation/getting-started/gs_tbu_contract_address.png) 06. Specify the time schedule, for example every 5 minutes. Paste the cron expression `*/5 * * * *`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) under _Cron expression_ or select one of the example timers. Then click **Next**. 07. To learn more about CRON expressions, click [here](https://docs.chain.link/chainlink-automation/guides/job-scheduler#specifying-the-time-schedule). 08. Enter an _Upkeep name_, your public key address under _Admin Address_, `500000`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) under _Gas limit_, and `0.1`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) under _Starting balance (LINK)_. ![](https://docs.chain.link/images/automation/getting-started/gs_tbu_upkeep_details.png) 09. Click **Register Upkeep**. 10. After the transaction has completed, you can view the performs for your upkeep in the upkeep details. You have successfully automated your first time-based upkeep. To learn more about creating time-based upkeeps, read [here](https://docs.chain.link/chainlink-automation/guides/job-scheduler). **Increment a counter using custom logic stored onchain.** 1. Navigate to the [Chainlink Automation app](https://automation.chain.link/) and connect to **Arbitrum Sepolia** in the top dropdown menu. 2. Connect your cryptocurrency wallet to the app if you haven't done so already. You might also need to fetch LINK for the Arbitrum Sepolia testnet from [faucets.chain.link](https://faucets.chain.link/arbitrum-sepolia). 3. Click **Register new Upkeep** and select **Custom logic** trigger. 4. Under _Target contract address_, enter [0x6C0AAAeBcDb6F5D03759B8BF14b47BE491755530](https://sepolia.arbiscan.io/address/0x6C0AAAeBcDb6F5D03759B8BF14b47BE491755530 "0x6C0AAAeBcDb6F5D03759B8BF14b47BE491755530")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg). This contract is an Automation-compatible contract that uses logic stored onchain and onchain state to determine when to increment a counter. View the source code [here](https://sepolia.arbiscan.io/address/0x6C0AAAeBcDb6F5D03759B8BF14b47BE491755530#readContract). Click **Next**. ![](https://docs.chain.link/images/automation/getting-started/gs_clu_contract_address.png) 5. Enter an _Upkeep name_, your public key address under _Admin Address_, `500000`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) under _Gas limit_, and `0.1`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) under _Starting balance (LINK)_. 6. Finally, enter your public key address under _Check data (Hexadecimal)_. `checkData` is optional static data that you can pass into your upkeep to ensure your counter increments. 7. Click **Register Upkeep**. 8. After the transaction is complete, you can view the performs for your upkeep in the upkeep details. Your upkeep should perform once every minute and stop after 4 performs. ![](https://docs.chain.link/images/automation/getting-started/gs_clu_upkeep_details.png) You have successfully automated your first custom logic upkeep. To learn more about creating custom logic upkeeps, read [here](https://docs.chain.link/chainlink-automation/guides/register-upkeep). **Increment an onchain counter using a log as trigger.** 01. Navigate to the [Chainlink Automation app](https://automation.chain.link/) and connect to **Arbitrum Sepolia** in the top dropdown menu. 02. Connect your cryptocurrency wallet to the app if you haven't done so already. You might also need to fetch LINK for the Arbitrum Sepolia testnet from [faucets.chain.link](https://faucets.chain.link/arbitrum-sepolia). 03. Click **Register new Upkeep** and select **Log trigger**. 04. Under _Contract to automate_, enter [0xe817e4A71C69C72C01B31906F9F8591FbaB6b448](https://sepolia.arbiscan.io/address/0xe817e4A71C69C72C01B31906F9F8591FbaB6b448 "0xe817e4A71C69C72C01B31906F9F8591FbaB6b448")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg). This is a simple `iLogAutomation`-compatible example contract that increments a counter when a log is detected. View the source code [here](https://sepolia.arbiscan.io/address/0xe817e4A71C69C72C01B31906F9F8591FbaB6b448#code). Click **Next**. 05. Under _Contract emitting logs_, enter [0x1260206b960bB07F12d48C19fad505CeFc071bDd](https://sepolia.arbiscan.io/address/0x1260206b960bB07F12d48C19fad505CeFc071bDd "0x1260206b960bB07F12d48C19fad505CeFc071bDd")![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg). This is the contract Automation will listen to for emitted logs. View the source code [here](https://sepolia.arbiscan.io/address/0x1260206b960bB07F12d48C19fad505CeFc071bDd#code). Click **Next**. ![](https://docs.chain.link/images/automation/getting-started/gs_ltu_log_trigger_details.png) 06. Under _Emitted log_ select **Bump** from the dropdown menu. This is the log signature Automation will look for. 07. _Log index topic filters_ are optional filters to narrow the logs you want to trigger your upkeep. For this example, enter your public key address under _addr_ and leave the _num_ field empty. Later when you call the _bump_ function to emit the log, your `msg.Sender` address will be emitted in the log, triggering your upkeep. Click **Next**. ![](https://docs.chain.link/images/automation/getting-started/gs_ltu_emit_log_details.png) 08. Enter an _Upkeep name_, your public key address under _Admin Address_, `500000`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) under _Gas limit_, and `0.1`![Copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) under _Starting balance (LINK)_. 09. Click **Register Upkeep** and wait for the transaction to complete. 10. To trigger your upkeep call _bump_ on the trigger contract by navigating to the Arbitrum Sepolia [scanner](https://sepolia.arbiscan.io/address/0x1260206b960bB07F12d48C19fad505CeFc071bDd#writeContract), connecting your wallet and executing the bump function. You can observe your upkeep's perform in the Automation dashboard. You have successfully automated your first log trigger upkeep. To learn more about creating log trigger upkeeps, read [here](https://docs.chain.link/chainlink-automation/guides/log-trigger). ## [Supported networks and costs](https://docs.chain.link/chainlink-automation/overview/getting-started\#supported-networks-and-costs) For a list of blockchains that are supported by Chainlink Automation, see the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) page. To learn more about the cost of using Chainlink Automation, see the [Automation Economics](https://docs.chain.link/chainlink-automation/overview/automation-economics) page. ## [Contact us](https://docs.chain.link/chainlink-automation/overview/getting-started\#contact-us) For help with your specific use case, [contact us](https://chain.link/contact?ref_id=Automation) to connect with one of our Solutions Architects. You can also ask questions about Chainlink Automation on [Stack Overflow](https://stackoverflow.com/questions/ask?tags=chainlink) or the [#automation channel](https://discord.com/channels/592041321326182401/821350860302581771) in our [Discord server](https://discord.gg/qj9qarT). [Utility contracts](https://docs.chain.link/chainlink-automation/tutorials/eth-balance) can also help you get started quickly. ## What's next - [\> Create Automation-Compatible Contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) - [\> Access Data Streams Using Automation](https://docs.chain.link/chainlink-automation/guides/streams-lookup) - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) - [\> Automation Billing and Costs](https://docs.chain.link/chainlink-automation/overview/automation-economics) ## Get the latest Chainlink content straight to your inbox. Email Address ## Debugging Chainlink Upkeeps [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Debugging and Troubleshooting Upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#overview) Given an upkeep ID, this page contains different methods of understanding and fixing issues with Upkeeps. ## [Automation debugging script](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#automation-debugging-script) You can use the [Automation debugging script](https://github.com/smartcontractkit/chainlink/blob/develop/core/scripts/chaincli/DEBUGGING.md) to debug and diagnose possible issues with registered upkeeps in Automation 2.1 registries. The script can debug custom logic upkeeps, log trigger upkeeps, and upkeeps that use `StreamsLookup`. ## [Underfunded upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#underfunded-upkeeps) In the Chainlink Automation app, you can see the registration details of the upkeep, alongside the balance required and `performUpkeep` history. If the upkeep is underfunded, you will see a warning on top of the page. Underfunded upkeeps will not be performed. ![](https://docs.chain.link/images/automation/debugging-ui.png) ## [Insufficient perform gas](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#insufficient-perform-gas) If your performGasLimit is too low, the Automation Network will not execute your upkeep as it will fail in simulation. Consider increasing your perform upkeep gas limit in the UI. See [supported Blockchain Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) for limits. ## [Insufficient check gas](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#insufficient-check-gas) If the amount of computation in your checkUpkeep exceeds our checkGasLimit, your upkeep will not be performed. You will have to reduce the amount of compute in checkUpkeep to bring the gas below the applicable limits. See [supported Blockchain Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) for limits. ## [Paused upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#paused-upkeeps) If your upkeep is paused, your upkeep will not be performed. Please unpause it in the [Chainlink Automation app](https://automation.chain.link/). ## [StreamsLookup](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#streamslookup) ### [Upkeep has not been allowlisted](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#upkeep-has-not-been-allowlisted) Once you registered your upkeep, you need to [ask the Data Streams team to allowlist your upkeepID](https://chainlinkcommunity.typeform.com/datastreams?#ref_id=docs) and specify the feeds you will need to access. If your upkeepID has not been added to the allow list it will not perform an upkeep. ### [Requesting multiple feeds where one is not valid](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#requesting-multiple-feeds-where-one-is-not-valid) It is possible to request multiple feeds by specifying the feeds in a string array. However, if one of the reports is invalid or not available then Automation will not return any values to your `checkCallback` function. This is to ensure correct execution of your contract. ### [StreamsLookup ErrorHandler](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#streamslookup-errorhandler) Handle the StreamsLookup upkeep [error codes](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler#error-codes) by using the StreamsLookup ErrorHandler. ## [Etherscan](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#etherscan) You can view the registry or user's upkeep address on [Etherscan](https://etherscan.io/) to view transaction history. There are three types of information you can find on Etherscan: - **All transactions on registry**: This shows all the perform upkeeps on the registry. You can view a specific `performUpkeep` transaction to debug more. - **Specific `performUpkeep` transaction**: By diving deep in the logs, you can check the upkeep ID within the UpkeepPerformed log. - **Target's internal transactions**: For a particular target contract, you can view its internal transactions which contains `performUpkeep` transactions for that contract by using the registry address as the filter for _from_ address. _Note_: internal transactions are only visible on Etherscan. ## [Tenderly](https://docs.chain.link/chainlink-automation/reference/debugging-errors\#tenderly) You can use [Tenderly](https://tenderly.co/) to simulate `checkUpkeep` and/or `performUpkeep` on different blocks. Before following the steps below, make sure you have a Tenderly account. 1. Enter the address of your selected registry. You can find this on the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) page. 2. Select your network. 3. Click **Fetch ABI** to automatically fetch the registry ABI. 4. Select the `checkUpkeep` function or `performUpkeep` function. 5. Enter the ID of your Upkeep. You can find this in the [Chainlink Automation app](https://automation.chain.link/). 6. You can either enter a block number to simulate a past action or use a pending block number to view the current state of an action and view the end result of an of an action. 7. Once the simulation is complete, you will see the result. This will be either a _success_ or an _error_. To understand errors, view information under the **Debug** tab. **Note**: if the `performUpkeep` is failing while the check is succeeding, Chainlink Automation will not broadcast transactions. ## What's next - [\> Build Flexible Smart Contracts Using Automation](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps) - [\> Manage your Upkeeps](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Overview [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Automation Concepts](https://docs.chain.link/chainlink-automation/concepts/automation-concepts\#overview) Before you explore how Chainlink Automation works on the [architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) page, you should explore core concepts. **Prerequisites**: - [Smart contracts](https://chain.link/education/smart-contracts#:~:text=DEFINITION,executed%20on%20a%20blockchain%20network.) - [ERC-677 Token standard](https://docs.chain.link/resources/link-token-contracts) ## [Upkeeps and triggers](https://docs.chain.link/chainlink-automation/concepts/automation-concepts\#upkeeps-and-triggers) These are the jobs or tasks that you execute onchain. For example, you can call a smart contract function if a specific set of conditions are met. These specific conditions are called _triggers_. There are currently three types of triggers that the Chainlink Automation Network supports including: - [**Time-based trigger**](https://docs.chain.link/chainlink-automation/guides/job-scheduler): Use a [time-based trigger](https://docs.chain.link/chainlink-automation/guides/job-scheduler) to execute your function according to a time schedule. This feature is also called the Job Scheduler and it is a throwback to the Ethereum Alarm Clock. Time-based trigger contracts do not need to be [compatible](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) with the `AutomationCompatibleInterface` contract. - [**Custom logic trigger**](https://docs.chain.link/chainlink-automation/guides/register-upkeep): Use a [custom logic trigger](https://docs.chain.link/chainlink-automation/guides/register-upkeep) to provide custom solidity logic that Automation Nodes evaluate (offchain) to determine when to execute your function onchain. Your contract must meet the requirements to be [compatible](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) with the `AutomationCompatibleInterface` contract. Custom logic examples include checking the balance on a contract, only executing limit orders when their levels are met, any one of our [coded examples](https://docs.chain.link/chainlink-automation/util-overview), and many more. - [**Log trigger**](https://docs.chain.link/chainlink-automation/guides/log-trigger): Use log data as both trigger and input. Your contract must meet the requirements to be [compatible](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) with the `AutomationCompatibleInterface` contract. ## [Automation nodes](https://docs.chain.link/chainlink-automation/concepts/automation-concepts\#automation-nodes) Automation Nodes in the Chainlink Automation Network provide service to upkeeps that are funded and registered in the Automation registry. Automation Nodes use the same Node Operators as Chainlink Data Feeds. ## What's next - [\> Automation Contracts](https://docs.chain.link/chainlink-automation/reference/automation-contracts) - [\> Automation Interfaces](https://docs.chain.link/chainlink-automation/reference/automation-interfaces) - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Best Practices [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Automation Best Practices](https://docs.chain.link/chainlink-automation/concepts/best-practice\#overview) This guide outlines the best practices when using Chainlink Automation. These best practices are important for using Chainlink Automation securely and reliably when you [create Automation-compatible contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts). ### [Use the latest version of Chainlink Automation](https://docs.chain.link/chainlink-automation/concepts/best-practice\#use-the-latest-version-of-chainlink-automation) To get the best reliability and security guarantees for your upkeep, use the latest version of Chainlink Automation. ### [Make registry and registrar addresses configurable](https://docs.chain.link/chainlink-automation/concepts/best-practice\#make-registry-and-registrar-addresses-configurable) Where your upkeep calls the registry or the registrar, you must make the address configurable so you can migrate your upkeep easily with the one-click migration capability. If you don't make the address configurable, you must redeploy the upkeep for migrations. Alternatively, set the forwarder address when your upkeep is deployed and read the registry from the forwarder during your execution to simplify it. ### [Use the Forwarder](https://docs.chain.link/chainlink-automation/concepts/best-practice\#use-the-forwarder) If your upkeep performs **sensitive** functions in your protocol, consider using the [`Forwarder`](https://docs.chain.link/chainlink-automation/overview/automation-release-notes) to lock it down so `performUpkeep` can only be called by the `Forwarder`. Add other permissible addresses if you need to call it yourself. Note the forwarder is only determined after registration so make this a mutable variable and ensure you add a setter function with permissions for you to set it. ### [Verify Data Streams reports fetched with StreamsLookup](https://docs.chain.link/chainlink-automation/concepts/best-practice\#verify-data-streams-reports-fetched-with-streamslookup) If your upkeep uses [StreamsLookup](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#streamslookupcompatibleinterface), ensure you use the [verification interface](https://docs.chain.link/data-streams/reference/interfaces) to verify your reports onchain. ### [Avoid "flickering" custom logic upkeeps](https://docs.chain.link/chainlink-automation/concepts/best-practice\#avoid-flickering-custom-logic-upkeeps) The Automation DON evaluates your upkeep regularly. When your upkeep is eligible, the DON attempts to perform the upkeep. For best results, ensure that `checkUpkeep` remains true until execution. If the state of your upkeep "flickers" by rapidly alternating between true and false, then your upkeep is at risk of not being performed. "Flickering" upkeeps might cause unintended consequences because there is latency between observing the state of the chain, getting consensus (v2 and later) on what needs to happen, and confirming the transaction onchain. ### [Always test your contracts](https://docs.chain.link/chainlink-automation/concepts/best-practice\#always-test-your-contracts) As with all smart contract testing, it is important to test the boundaries of your smart contract in order to ensure it operates as intended. Similarly, it is important to make sure the compatible contract operates within the parameters of the `Registry`. Test all of your mission-critical contracts, and stress-test the contract to confirm the performance and correct operation of your use case under load and adversarial conditions. The Chainlink Automation Network will continue to operate under stress, but so should your contract. For a list of supported testnet blockchains, please review the [supported networks page](https://docs.chain.link/chainlink-automation/overview/supported-networks). ### [Using ERC-677 tokens](https://docs.chain.link/chainlink-automation/concepts/best-practice\#using-erc-677-tokens) For registration on Mainnet, you need ERC-677 LINK. Many token bridges give you ERC-20 LINK tokens. Use PegSwap to [convert Chainlink tokens (LINK) to be ERC-677 compatible](https://pegswap.chain.link/). To register on a supported testnet, get [LINK](https://docs.chain.link/resources/link-token-contracts) for the testnet that you want to use from our faucet. ### [Automation v1 upkeep revalidation](https://docs.chain.link/chainlink-automation/concepts/best-practice\#automation-v1-upkeep-revalidation) If your upkeep is on Automation v1, we recommend that you revalidate the conditions specified in `checkUpkeep` in your `performUpkeep` function. Automation v1 uses a turn taking system where nodes rotate to monitor your upkeep. It does not use consensus. ## What's next - [\> Build Flexible Smart Contracts Using Automation](https://docs.chain.link/chainlink-automation/guides/flexible-upkeeps) - [\> Manage your Upkeeps](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps) ## Get the latest Chainlink content straight to your inbox. Email Address ## Manage Chainlink Upkeeps [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Managing Upkeeps](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps\#overview) Manage your Upkeeps to get the best performance. ## [Fund your upkeep](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps\#fund-your-upkeep) You must monitor the balance of your Upkeep. If the Upkeep LINK balance drops below the [minimum balance](https://docs.chain.link/chainlink-automation/overview/automation-economics#minimum-balance), the Chainlink Automation Network will not perform the Upkeep. Follow these steps to fund your Upkeep: 1. **Click `View Upkeep`** or go to the [Chainlink Automation App](https://automation.chain.link/) and click on your recently registered Upkeep under My Upkeeps. 2. **Click the `Add funds` button** 3. **Approve the LINK spend allowance**![Approve LINK Spend Allowance](https://docs.chain.link/images/automation/automation-approve-allowance.png) 4. **Confirm the LINK transfer** by sending funds to the Chainlink Automation Network Registry ![Confirm LINK Transfer](https://docs.chain.link/images/automation/automation-confirm-transfer.png) 5. **Receive a success message** and verify that the funds were added to the Upkeep ![Funds Added Successful Message](https://docs.chain.link/images/automation/automation-add-funds.png) ## [Maintain a minimum balance](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps\#maintain-a-minimum-balance) Each Upkeep has a [minimum balance](https://docs.chain.link/chainlink-automation/overview/automation-economics#minimum-balance) to ensure that an Upkeeps will still run should a sudden spike occur. If your Upkeep LINK balance drops below this amount, the Upkeep will not be performed. To account for Upkeep execution over time and possible extended gas spikes, maintain an Upkeep LINK balance that is 3 to 5 times the minimum balance. Note if you have an upkeep that performs frequently you may want to increase the buffer to ensure a reasonable interval before you need to fund again. Developers also have the ability to update `performGasLimit` for an upkeep. ## [Withdraw funds](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps\#withdraw-funds) To withdraw funds, the Upkeep administrator have to cancel the Upkeep first. There is delay once an Upkeep has been cancelled before funds can be withdrawn. The number of blocks delay varies by network and once the delay has passed, you can **Withdraw funds**. ## [Interacting directly with the Chainlink Automation Registry](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps\#interacting-directly-with-the-chainlink-automation-registry) After registration, you can interact directly with the [registry contract](https://docs.chain.link/chainlink-automation/overview/supported-networks) functions such as `cancelUpkeep` and `addFunds` using your **Upkeep ID**. The Registry Address might change when new contracts are deployed with new functionality. ## What's next - [\> Troubleshoot and Debug Upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors) - [\> Automation Billing and Costs](https://docs.chain.link/chainlink-automation/overview/automation-economics) ## Get the latest Chainlink content straight to your inbox. Email Address ## Vault Harvester Automation [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Vault Harvester](https://docs.chain.link/chainlink-automation/tutorials/vault-harvester\#overview) Harvesting and compounding is key to maximize yield in DeFi yield aggregators. Automate harvesting and compounding using Chainlink Automation's decentralized automation network. View the template [here](https://github.com/smartcontractkit/chainlink-automation-templates/tree/main/vault-harvester#chainlink-keepers-template-vault-harvester). Below is the main contract `KeeperCompatibleHarvester.sol`: ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity //SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "../libraries/UpkeepLibrary.sol"; import "../interfaces/IKeeperRegistry.sol"; import "../interfaces/IHarvester.sol"; abstract contract KeeperCompatibleHarvester is IHarvester, Ownable { using SafeERC20 for IERC20; // Contracts. IKeeperRegistry public keeperRegistry; // Configuration state variables. uint256 public performUpkeepGasLimit; uint256 public performUpkeepGasLimitBuffer; uint256 public vaultHarvestFunctionGasOverhead; // Estimated average gas cost of calling harvest() uint256 public keeperRegistryGasOverhead; // Gas cost of upstream contract that calls performUpkeep(). This is a private variable on KeeperRegistry. uint256 public chainlinkUpkeepTxPremiumFactor; // Tx premium factor/multiplier scaled by 1 gwei (10**9). address public callFeeRecipient; // State variables that will change across upkeeps. uint256 public startIndex; constructor( address _keeperRegistry, uint256 _performUpkeepGasLimit, uint256 _performUpkeepGasLimitBuffer, uint256 _vaultHarvestFunctionGasOverhead, uint256 _keeperRegistryGasOverhead ) { // Set contract references. keeperRegistry = IKeeperRegistry(_keeperRegistry); // Initialize state variables from initialize() arguments. performUpkeepGasLimit = _performUpkeepGasLimit; performUpkeepGasLimitBuffer = _performUpkeepGasLimitBuffer; vaultHarvestFunctionGasOverhead = _vaultHarvestFunctionGasOverhead; keeperRegistryGasOverhead = _keeperRegistryGasOverhead; // Initialize state variables derived from initialize() arguments. (, OnchainConfig memory config, , , ) = keeperRegistry.getState(); chainlinkUpkeepTxPremiumFactor = uint256(config.paymentPremiumPPB); } /* */ /* checkUpkeep */ /* */ function checkUpkeep( bytes calldata _checkData // unused ) external view override returns ( bool upkeepNeeded, bytes memory performData // array of vaults + ) { _checkData; // dummy reference to get rid of unused parameter warning // get vaults to iterate over address[] memory vaults = _getVaultAddresses(); // count vaults to harvest that will fit within gas limit (HarvestInfo[] memory harvestInfo, uint256 numberOfVaultsToHarvest, uint256 newStartIndex) = _countVaultsToHarvest( vaults ); if (numberOfVaultsToHarvest == 0) return (false, bytes("No vaults to harvest")); (address[] memory vaultsToHarvest, uint256 heuristicEstimatedTxCost, uint256 callRewards) = _buildVaultsToHarvest( vaults, harvestInfo, numberOfVaultsToHarvest ); uint256 nonHeuristicEstimatedTxCost = _calculateExpectedTotalUpkeepTxCost(numberOfVaultsToHarvest); performData = abi.encode( vaultsToHarvest, newStartIndex, heuristicEstimatedTxCost, nonHeuristicEstimatedTxCost, callRewards ); return (true, performData); } function _buildVaultsToHarvest( address[] memory _vaults, HarvestInfo[] memory _willHarvestVaults, uint256 _numberOfVaultsToHarvest ) internal view returns (address[] memory vaultsToHarvest, uint256 heuristicEstimatedTxCost, uint256 totalCallRewards) { uint256 vaultPositionInArray; vaultsToHarvest = new address[](_numberOfVaultsToHarvest); // create array of vaults to harvest. Could reduce code duplication from _countVaultsToHarvest via a another function parameter called _loopPostProcess for (uint256 offset; offset < _vaults.length; ++offset) { uint256 vaultIndexToCheck = UpkeepLibrary._getCircularIndex(startIndex, offset, _vaults.length); address vaultAddress = _vaults[vaultIndexToCheck]; HarvestInfo memory harvestInfo = _willHarvestVaults[offset]; if (harvestInfo.willHarvest) { vaultsToHarvest[vaultPositionInArray] = vaultAddress; heuristicEstimatedTxCost += harvestInfo.estimatedTxCost; totalCallRewards += harvestInfo.callRewardsAmount; vaultPositionInArray += 1; } // no need to keep going if we're past last index if (vaultPositionInArray == _numberOfVaultsToHarvest) break; } return (vaultsToHarvest, heuristicEstimatedTxCost, totalCallRewards); } function _countVaultsToHarvest( address[] memory _vaults ) internal view returns (HarvestInfo[] memory harvestInfo, uint256 numberOfVaultsToHarvest, uint256 newStartIndex) { uint256 gasLeft = _calculateAdjustedGasCap(); uint256 vaultIndexToCheck; // hoisted up to be able to set newStartIndex harvestInfo = new HarvestInfo[](_vaults.length); // count the number of vaults to harvest. for (uint256 offset; offset < _vaults.length; ++offset) { // _startIndex is where to start in the _vaultRegistry array, offset is position from start index (in other words, number of vaults we've checked so far), // then modulo to wrap around to the start of the array, until we've checked all vaults, or break early due to hitting gas limit // this logic is contained in _getCircularIndex() vaultIndexToCheck = UpkeepLibrary._getCircularIndex(startIndex, offset, _vaults.length); address vaultAddress = _vaults[vaultIndexToCheck]; (bool willHarvest, uint256 estimatedTxCost, uint256 callRewardsAmount) = _willHarvestVault(vaultAddress); if (willHarvest && gasLeft >= vaultHarvestFunctionGasOverhead) { gasLeft -= vaultHarvestFunctionGasOverhead; numberOfVaultsToHarvest += 1; harvestInfo[offset] = HarvestInfo(true, estimatedTxCost, callRewardsAmount); } if (gasLeft < vaultHarvestFunctionGasOverhead) { break; } } newStartIndex = UpkeepLibrary._getCircularIndex(vaultIndexToCheck, 1, _vaults.length); return (harvestInfo, numberOfVaultsToHarvest, newStartIndex); } function _willHarvestVault(address _vaultAddress) internal view returns (bool willHarvestVault, uint256, uint256) { (bool shouldHarvestVault, uint256 estimatedTxCost, uint256 callRewardAmount) = _shouldHarvestVault(_vaultAddress); bool canHarvestVault = _canHarvestVault(_vaultAddress); willHarvestVault = canHarvestVault && shouldHarvestVault; return (willHarvestVault, estimatedTxCost, callRewardAmount); } function _canHarvestVault(address _vaultAddress) internal view virtual returns (bool canHarvest); function _shouldHarvestVault( address _vaultAddress ) internal view virtual returns (bool shouldHarvestVault, uint256 txCostWithPremium, uint256 callRewardAmount); /* */ /* performUpkeep */ /* */ function performUpkeep(bytes calldata _performData) external override { ( address[] memory vaultsToHarvest, uint256 newStartIndex, uint256 heuristicEstimatedTxCost, uint256 nonHeuristicEstimatedTxCost, uint256 estimatedCallRewards ) = abi.decode(_performData, (address[], uint256, uint256, uint256, uint256)); _runUpkeep( vaultsToHarvest, newStartIndex, heuristicEstimatedTxCost, nonHeuristicEstimatedTxCost, estimatedCallRewards ); } function _runUpkeep( address[] memory _vaults, uint256 _newStartIndex, uint256 _heuristicEstimatedTxCost, uint256 _nonHeuristicEstimatedTxCost, uint256 _estimatedCallRewards ) internal { // Make sure estimate looks good. if (_estimatedCallRewards < _nonHeuristicEstimatedTxCost) { emit HeuristicFailed( block.number, _heuristicEstimatedTxCost, _nonHeuristicEstimatedTxCost, _estimatedCallRewards ); } uint256 gasBefore = gasleft(); // multi harvest require(_vaults.length > 0, "No vaults to harvest"); (uint256 numberOfSuccessfulHarvests, uint256 numberOfFailedHarvests, uint256 calculatedCallRewards) = _multiHarvest( _vaults ); // ensure _newStartIndex is valid and set startIndex uint256 vaultCount = _getVaultAddresses().length; require(_newStartIndex >= 0 && _newStartIndex < vaultCount, "_newStartIndex out of range."); startIndex = _newStartIndex; uint256 gasAfter = gasleft(); uint256 gasUsedByPerformUpkeep = gasBefore - gasAfter; // split these into their own functions to avoid `Stack too deep` _reportProfitSummary( gasUsedByPerformUpkeep, _nonHeuristicEstimatedTxCost, _estimatedCallRewards, calculatedCallRewards ); _reportHarvestSummary(_newStartIndex, gasUsedByPerformUpkeep, numberOfSuccessfulHarvests, numberOfFailedHarvests); } function _reportHarvestSummary( uint256 _newStartIndex, uint256 _gasUsedByPerformUpkeep, uint256 _numberOfSuccessfulHarvests, uint256 _numberOfFailedHarvests ) internal { emit HarvestSummary( block.number, // state variables startIndex, _newStartIndex, // gas metrics tx.gasprice, _gasUsedByPerformUpkeep, // summary metrics _numberOfSuccessfulHarvests, _numberOfFailedHarvests ); } function _reportProfitSummary( uint256 _gasUsedByPerformUpkeep, uint256 _nonHeuristicEstimatedTxCost, uint256 _estimatedCallRewards, uint256 _calculatedCallRewards ) internal { uint256 estimatedTxCost = _nonHeuristicEstimatedTxCost; // use nonHeuristic here as its more accurate uint256 estimatedProfit = UpkeepLibrary._calculateProfit(_estimatedCallRewards, estimatedTxCost); uint256 calculatedTxCost = _calculateTxCostWithOverheadWithPremium(_gasUsedByPerformUpkeep); uint256 calculatedProfit = UpkeepLibrary._calculateProfit(_calculatedCallRewards, calculatedTxCost); emit ProfitSummary( // predicted values estimatedTxCost, _estimatedCallRewards, estimatedProfit, // calculated values calculatedTxCost, _calculatedCallRewards, calculatedProfit ); } function _multiHarvest( address[] memory _vaults ) internal returns (uint256 numberOfSuccessfulHarvests, uint256 numberOfFailedHarvests, uint256 cumulativeCallRewards) { bool[] memory isSuccessfulHarvest = new bool[](_vaults.length); for (uint256 i = 0; i < _vaults.length; ++i) { (bool didHarvest, uint256 callRewards) = _harvestVault(_vaults[i]); // Add rewards to cumulative tracker. if (didHarvest) { isSuccessfulHarvest[i] = true; cumulativeCallRewards += callRewards; } } (address[] memory successfulHarvests, address[] memory failedHarvests) = _getSuccessfulAndFailedVaults( _vaults, isSuccessfulHarvest ); emit SuccessfulHarvests(block.number, successfulHarvests); emit FailedHarvests(block.number, failedHarvests); numberOfSuccessfulHarvests = successfulHarvests.length; numberOfFailedHarvests = failedHarvests.length; return (numberOfSuccessfulHarvests, numberOfFailedHarvests, cumulativeCallRewards); } function _harvestVault(address _vault) internal virtual returns (bool didHarvest, uint256 callRewards); function _getSuccessfulAndFailedVaults( address[] memory _vaults, bool[] memory _isSuccessfulHarvest ) internal pure returns (address[] memory successfulHarvests, address[] memory failedHarvests) { uint256 successfulCount; for (uint256 i = 0; i < _vaults.length; i++) { if (_isSuccessfulHarvest[i]) { successfulCount += 1; } } successfulHarvests = new address[](successfulCount); failedHarvests = new address[](_vaults.length - successfulCount); uint256 successfulHarvestsIndex; uint256 failedHarvestIndex; for (uint256 i = 0; i < _vaults.length; i++) { if (_isSuccessfulHarvest[i]) { successfulHarvests[successfulHarvestsIndex++] = _vaults[i]; } else { failedHarvests[failedHarvestIndex++] = _vaults[i]; } } return (successfulHarvests, failedHarvests); } /* */ /* Set */ /* */ function setPerformUpkeepGasLimit(uint256 _performUpkeepGasLimit) external override onlyOwner { performUpkeepGasLimit = _performUpkeepGasLimit; } function setPerformUpkeepGasLimitBuffer(uint256 _performUpkeepGasLimitBuffer) external override onlyOwner { performUpkeepGasLimitBuffer = _performUpkeepGasLimitBuffer; } function setHarvestGasConsumption(uint256 _harvestGasConsumption) external override onlyOwner { vaultHarvestFunctionGasOverhead = _harvestGasConsumption; } /* */ /* View */ /* */ function _getVaultAddresses() internal view virtual returns (address[] memory); function _getVaultHarvestGasOverhead(address _vault) internal view virtual returns (uint256); function _calculateAdjustedGasCap() internal view returns (uint256 adjustedPerformUpkeepGasLimit) { return performUpkeepGasLimit - performUpkeepGasLimitBuffer; } function _calculateTxCostWithPremium(uint256 _gasOverhead) internal view returns (uint256 txCost) { return UpkeepLibrary._calculateUpkeepTxCost(tx.gasprice, _gasOverhead, chainlinkUpkeepTxPremiumFactor); } function _calculateTxCostWithOverheadWithPremium( uint256 _totalVaultHarvestOverhead ) internal view returns (uint256 txCost) { return UpkeepLibrary._calculateUpkeepTxCostFromTotalVaultHarvestOverhead( tx.gasprice, _totalVaultHarvestOverhead, keeperRegistryGasOverhead, chainlinkUpkeepTxPremiumFactor ); } function _calculateExpectedTotalUpkeepTxCost( uint256 _numberOfVaultsToHarvest ) internal view returns (uint256 txCost) { uint256 totalVaultHarvestGasOverhead = vaultHarvestFunctionGasOverhead * _numberOfVaultsToHarvest; return UpkeepLibrary._calculateUpkeepTxCostFromTotalVaultHarvestOverhead( tx.gasprice, totalVaultHarvestGasOverhead, keeperRegistryGasOverhead, chainlinkUpkeepTxPremiumFactor ); } function _estimateSingleVaultHarvestGasOverhead( uint256 _vaultHarvestFunctionGasOverhead ) internal view returns (uint256 totalGasOverhead) { totalGasOverhead = _vaultHarvestFunctionGasOverhead + keeperRegistryGasOverhead; } /* */ /* Misc */ /* */ /** * @dev Rescues random funds stuck. * @param _token address of the token to rescue. */ function inCaseTokensGetStuck(address _token) external onlyOwner { IERC20 token = IERC20(_token); uint256 amount = token.balanceOf(address(this)); token.safeTransfer(msg.sender, amount); } } ``` This is an abstract contract that iterates vaults from a provided list and fits vaults within upkeep gas limit. The contract also provides helper functions to calculate gas consumption and estimate profit and contains a trigger mechanism can be time-based, profit-based or custom. Finally, the contract reports profits, successful harvests, and failed harvests. ## What's next - [\> Automate the Batch Reveal of Collections](https://docs.chain.link/quickstarts/batch-reveal) - [\> Create Dynamic NFTs](https://docs.chain.link/quickstarts/dynamic-metadata) - [\> Automate Top-Up for Contract Balances](https://docs.chain.link/quickstarts/eth-balance-monitor) - [\> Automation Top-Up for VRF Subscriptions](https://docs.chain.link/quickstarts/vrf-subscription-monitor) ## Get the latest Chainlink content straight to your inbox. Email Address ## Time-Based Upkeep Guide [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Create a Time-Based Upkeep](https://docs.chain.link/chainlink-automation/guides/job-scheduler\#overview) Create powerful automation for your smart contract using time schedules without having to create Automation-compatible contracts. This guide explains how to register time-based upkeeps. ![Job Scheduler animation](https://docs.chain.link/images/automation/auto-job-scheduler.gif) ## [Using the Chainlink Automation app](https://docs.chain.link/chainlink-automation/guides/job-scheduler\#using-the-chainlink-automation-app) In the [Chainlink Automation App](https://automation.chain.link/), click the blue **Register new Upkeep** button. ![](https://docs.chain.link/images/automation/auto-ui-home.png) ### [Connecting your wallet](https://docs.chain.link/chainlink-automation/guides/job-scheduler\#connecting-your-wallet) If you do not already have a wallet connected with the Chainlink Automation network, the interface will prompt you to do so. Click the **Connect Wallet** button and follow the remaining prompts to connect your wallet to one of the [Automation supported blockchain networks](https://docs.chain.link/chainlink-automation/overview/supported-networks). ![](https://docs.chain.link/images/automation/auto-ui-wallet.png) ## [Trigger selection](https://docs.chain.link/chainlink-automation/guides/job-scheduler\#trigger-selection) Select **Time-based** trigger. ![](https://docs.chain.link/images/automation/ui_select_trigger.png) ## [Using time-based triggers](https://docs.chain.link/chainlink-automation/guides/job-scheduler\#using-time-based-triggers) When you select the time-based trigger, you are prompted to enter a _contract address_. Provide the address of the contract you want to automate. If you did not verify the contract on chain, you will need to paste the [Application Binary Interface](https://docs.soliditylang.org/en/develop/abi-spec.html) (ABI) of the deployed contract into the corresponding text box. Select the function name that you want to execute and provide any static inputs. If you want to use dynamic inputs please see [Custom logic Upkeeps](https://docs.chain.link/chainlink-automation/guides/register-upkeep) ![](https://docs.chain.link/images/automation/automation-time-based-trigger.png) ### [Specifying the time schedule](https://docs.chain.link/chainlink-automation/guides/job-scheduler\#specifying-the-time-schedule) After you have successfully entered your contract address and ABI, specify your time schedule in the form of a [CRON expression](https://en.wikipedia.org/wiki/Cron). CRON expressions provide a shorthand way of creating a time schedule. You can use the provided example buttons in the Automation app to experiment with different schedules. Then, create your own time schedule. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```plaintext Cron jobs are interpreted according to this format: ┌───────────── minute (0 - 59) │ ┌───────────── hour (0 - 23) │ │ ┌───────────── day of the month (1 - 31) │ │ │ ┌───────────── month (1 - 12) │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ * * * * * All times are in UTC. - can be used for range e.g. "0 8-16 * * *" / can be used for interval e.g. "0 */2 * * *" , can be used for list e.g. "0 17 * * 0,2,4" Special limitations: * there is no year field * no special characters: ? L W # * lists can have a max length of 26 * no words like JAN / FEB or MON / TUES ``` After entering your CRON expression, click **Next**. ![](https://docs.chain.link/images/automation/automation-cron-expression.png) ## [Entering upkeep details](https://docs.chain.link/chainlink-automation/guides/job-scheduler\#entering-upkeep-details) Provide the following information in the Automation app: - **Upkeep name**: This will be visible in the Chainlink Automation app. - **Gas limit**: This is the maximum amount of gas that your transaction requires to execute on chain. This limit cannot exceed the `performGasLimit` value configured on the [registry](https://docs.chain.link/chainlink-automation/overview/supported-networks). - **Starting balance (LINK)**: Specify a LINK starting balance to fund your upkeep. See the [LINK Token Contracts](https://docs.chain.link/resources/link-token-contracts) page to find the correct contract address and access faucets for testnet LINK. This field is required. You must have LINK before you can use the Chainlink Automation service. - **Your email address (optional)**: This email address will be used to send you an email notification when your upkeep is underfunded. ![](https://docs.chain.link/images/automation/automation-upkeep-details.png) ## [Complete upkeep registration](https://docs.chain.link/chainlink-automation/guides/job-scheduler\#complete-upkeep-registration) Click **Register upkeep** and confirm the transaction in MetaMask. ![Upkeep Registration Success Message](https://docs.chain.link/images/automation/automation-registration-submitted.png) Your upkeeps will be displayed in your list of **Active Upkeeps**. You must monitor the balance of your upkeep. If the balance drops below the **minimum balance**, the Chainlink Automation Network will not perform the Upkeep. See [Managing Upkeeps](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps) to learn how to manage your upkeeps. ## What's next - [\> Register Custom Logic Upkeeps](https://docs.chain.link/chainlink-automation/guides/register-upkeep) - [\> Register Log Trigger Upkeeps](https://docs.chain.link/chainlink-automation/guides/log-trigger) - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) - [\> Billing and Costs](https://docs.chain.link/chainlink-automation/overview/automation-economics) ## Get the latest Chainlink content straight to your inbox. Email Address ## Log Trigger Upkeep Guide [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Register a Log Trigger Upkeep](https://docs.chain.link/chainlink-automation/guides/log-trigger\#overview) Create powerful smart contracts that use log data as both trigger and input. This guide explains how to create log-trigger upkeeps. ## [Understanding maximum logs processed](https://docs.chain.link/chainlink-automation/guides/log-trigger\#understanding-maximum-logs-processed) Chainlink Automation processes a limited number of logs per block per upkeep. See the [Service Limits](https://docs.chain.link/chainlink-automation/overview/service-limits) page to learn about how logs are processed and how many logs you can expect to be processed per block on the chain you're using. ## [Emit a log](https://docs.chain.link/chainlink-automation/guides/log-trigger\#emit-a-log) 1. Open `CountEmitLog.sol` in Remix. This contract contains an event `WantsToCount` that keeps track of the address of the message sender. The function `emitCountLog` emits this event. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; contract CountEmitLog { event WantsToCount(address indexed msgSender); constructor() {} function emitCountLog() public { emit WantsToCount(msg.sender); } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/Automation/CountEmitLog.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) 1. Under _Environment_, select the option **Injected Provider** to connect to your cryptocurrency wallet. 2. Deploy the contract and confirm the transaction. 3. You can view the contract on Etherscan by clicking the message in the terminal. You an view the address of the created contract and the original contract in Etherscan. ![](https://docs.chain.link/images/automation/log-trig-addresses.png) 4. Navigate to the _Contract_ tab. If the contract is already verified, you will see options to **Read Contract** and **Write Contract**. If your contract isn't verified, follow the prompts in Etherscan to verify the contract. 5. Click **Write Contract** and click the **emitCountLog** button to emit a log. 6. Navigate back to Etherscan and locate the _Events_ tab. You should see the event of emitting a log recorded in this section. ![](https://docs.chain.link/images/automation/log-trig-event.png) ## [Using `ILogAutomation` Interface](https://docs.chain.link/chainlink-automation/guides/log-trigger\#using-ilogautomation-interface) 1. Open `CountWithLog.sol` in Remix. This contract contains a struct to account for the log structure and uses the [ILogAutomation interface](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#ilogautomation) for log automation. The interface contains the `checkLog` and `performUpkeep` functions. The contract contains an event `CountedBy`. The `counted` variable will be incremented when `performUpkeep` is called. ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; struct Log { uint256 index; // Index of the log in the block uint256 timestamp; // Timestamp of the block containing the log bytes32 txHash; // Hash of the transaction containing the log uint256 blockNumber; // Number of the block containing the log bytes32 blockHash; // Hash of the block containing the log address source; // Address of the contract that emitted the log bytes32[] topics; // Indexed topics of the log bytes data; // Data of the log } interface ILogAutomation { function checkLog( Log calldata log, bytes memory checkData ) external returns (bool upkeepNeeded, bytes memory performData); function performUpkeep(bytes calldata performData) external; } contract CountWithLog is ILogAutomation { event CountedBy(address indexed msgSender); uint256 public counted = 0; constructor() {} function checkLog( Log calldata log, bytes memory ) external pure returns (bool upkeepNeeded, bytes memory performData) { upkeepNeeded = true; address logSender = bytes32ToAddress(log.topics[1]); performData = abi.encode(logSender); } function performUpkeep(bytes calldata performData) external override { counted += 1; address logSender = abi.decode(performData, (address)); emit CountedBy(logSender); } function bytes32ToAddress(bytes32 _address) public pure returns (address) { return address(uint160(uint256(_address))); } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/Automation/CountWithLog.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) 1. Deploy the contract and confirm your transaction. 2. Under _Deployed Contracts_, expand `CountWithLog`. Click the **count** button to view the value of the count variable. It should be 0. ![](https://docs.chain.link/images/automation/log-trig-count-0.png) 3. Copy the address of this contract either via Remix or Etherscan to register it on the Chainlink Automation app. ## [Using the Chainlink Automation App](https://docs.chain.link/chainlink-automation/guides/log-trigger\#using-the-chainlink-automation-app) [Open the Chainlink Automation App](https://automation.chain.link/) **Click the Register New Upkeep button.** ![](https://docs.chain.link/images/automation/auto-ui-home.png) ### [Connecting your wallet](https://docs.chain.link/chainlink-automation/guides/log-trigger\#connecting-your-wallet) If you do not already have a wallet connected with the Chainlink Automation network, the interface will prompt you to do so. Click the **Connect Wallet** button and follow the remaining prompts to connect your wallet to one of the [Automation supported blockchain networks](https://docs.chain.link/chainlink-automation/overview/supported-networks). ![](https://docs.chain.link/images/automation/auto-ui-wallet.png) ## [Trigger selection](https://docs.chain.link/chainlink-automation/guides/log-trigger\#trigger-selection) Select **Log Trigger**. ![](https://docs.chain.link/images/automation/ui_select_trigger.png) ## [Using log triggers](https://docs.chain.link/chainlink-automation/guides/log-trigger\#using-log-triggers) 1. **Provide the address of your [Automation-compatible contract](https://docs.chain.link/chainlink-automation/guides/compatible-contracts)** that you want to automate. In this case, we will paste the address of `CountWithLog.sol`. This contract must follow the format of the [ILogAutomation interface](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol) to ensure Automation nodes can interact with your contract as expected. ![](https://docs.chain.link/images/automation/log_trig_1_upkeep_address.png) 2. **Provide the address of the contract that will be emitting the log.** In this case, this is the address of `CountEmitLog.sol`. If the contract is not validated you will need to provide the ABI. ![](https://docs.chain.link/images/automation/log_trig_2_emitter_address.png) To find the ABI of your contract in Remix, navigate to the Compiler view using the left side icons. Then, copy the ABI to your clipboard using the button at the bottom of the panel. ![](https://docs.chain.link/images/automation/log-trig-ebi.png) 3. **Use the dropdown to select the triggering event.** This is **WantsToCount**. You can also provide one optional filter per any of the indexed events in the log, but you don't have to. When this combination of filters are matched the upkeep will trigger. ![](https://docs.chain.link/images/automation/log_trig_3_logsig_filter_populated.png) ## [Entering upkeep details](https://docs.chain.link/chainlink-automation/guides/log-trigger\#entering-upkeep-details) Provide the following information in the Automation app: - **Upkeep name**: This will be visible in the Chainlink Automation app. - **Gas limit**: This is the maximum amount of gas that your transaction requires to execute on chain. This limit cannot exceed the `performGasLimit` value configured on the [registry](https://docs.chain.link/chainlink-automation/overview/supported-networks). - **Starting balance (LINK)**: Specify a LINK starting balance to fund your upkeep. See the [LINK Token Contracts](https://docs.chain.link/resources/link-token-contracts) page to find the correct contract address and access faucets for testnet LINK. This field is required. You must have LINK before you can use the Chainlink Automation service. - **Check data**: Optional input field that you may use depending on whether you are using it in your contract. - **Your email address (optional)**: This email address will be used to send you an email notification when your upkeep is underfunded. ## [Complete upkeep registration](https://docs.chain.link/chainlink-automation/guides/log-trigger\#complete-upkeep-registration) Click **Register upkeep** and confirm the transaction in MetaMask. ![Upkeep Registration Success Message](https://docs.chain.link/images/automation/automation-registration-submitted.png) Your upkeeps will be displayed in your list of **Active Upkeeps**. You must monitor the balance of your upkeep. If the balance drops below the **minimum balance**, the Chainlink Automation Network will not perform the Upkeep. See [Managing Upkeeps](https://docs.chain.link/chainlink-automation/guides/manage-upkeeps) to learn how to manage your upkeeps. ![](https://docs.chain.link/images/automation/log-trig-config.png) ## [Performing upkeep](https://docs.chain.link/chainlink-automation/guides/log-trigger\#performing-upkeep) Navigate back to the Etherscan page for `CountEmitLog.sol`. Under _Write Contract_, click the button to **emitCountLog**. Refresh the upkeep details page. You may have to wait a few moments. Under _History_, you should see the upkeep has been performed. ## What's next - [\> Automation Interfaces](https://docs.chain.link/chainlink-automation/reference/automation-interfaces) - [\> Register Upkeeps Programmatically](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract) - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) ## Get the latest Chainlink content straight to your inbox. Email Address ## Gas Price Threshold Guide [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Set a gas price threshold on your upkeep](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#overview) You can set a gas price threshold for your upkeep to prevent it from being serviced when gas prices exceed the _maximum gas price_ you specify. You can set a maximum gas price on conditional upkeeps and log trigger upkeeps. **Note:** This is a different gas setting than the upkeep gas limit, which limits the _amount_ of gas used. After you set your maximum gas price, it takes a few minutes to go into effect as it syncs with the nodes. Updating your maximum gas price does not impact any upkeep where the execution is in-flight. ## [Limitations](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#limitations) Do not set a gas price threshold when speed of execution is important. The gas price threshold can significantly delay the execution of conditional upkeeps and prevent execution entirely for log trigger upkeeps. ### [Gas prices spike after your transaction is in flight](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#gas-prices-spike-after-your-transaction-is-in-flight) Due to the decentralized nature of the Automation network and its transaction manager, your upkeep may be performed at a gas price higher than the maximum gas price you set. This edge case can happen when: 1. The Automation network determines that the upkeep should be performed while the onchain gas price is below your threshold. After that check occurs, the `performUpkeep` transaction is in flight (in the mempool). 2. After the transaction is already in flight, gas prices start spiking and move above your threshold. To ensure that the node's sending key nonce does not become blocked, the node automatically increases the gas to confirm the transaction and prevent the nonce from blocking subsequent transactions. To avoid this edge case, increase the buffer in your gas price threshold. ### [Log trigger upkeeps are not retried](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#log-trigger-upkeeps-are-not-retried) If a log trigger upkeep is triggered by a log event, and is declared ineligible to perform the upkeep because gas prices are above your gas price threshold, the upkeep is not retried. ## [Choose your maximum gas price](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#choose-your-maximum-gas-price) Each node compares the specified threshold to its estimate of onchain gas price. A quorum is needed before upkeeps are performed. Nodes may bid aggressively on gas prices to ensure transactions go through. If the node's gas price bid is above your maximum gas price, the node will not perform your upkeep. For example, if you set a gas price threshold of 3 gwei, your upkeep's execution may stop as soon as the node's gas price bid hits 3 gwei, even if the actual gas price is only 2 gwei. To adjust for this, you may need to set a higher gas price threshold. ## [Set the maximum gas price on an existing upkeep](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#set-the-maximum-gas-price-on-an-existing-upkeep) Set the maximum gas price using the `offchainConfig` field in your upkeep. Only the upkeep admin can set gas controls for an upkeep. This setting is not yet available in the Chainlink Automation App, so it must be done programmatically. To set the maximum gas price on an upkeep, follow these steps: 1. [Format and encode your offchain config](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold#format-and-encode-your-offchain-config). The offchain config is where you set your maximum gas price. 2. Run `setUpkeepOffchainConfig` on the registry using your upkeep ID and the encoded value of your offchain config. ### [Run the script](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#run-the-script) The [Automation gas threshold](https://github.com/smartcontractkit/smart-contract-examples/tree/main/automation-gas-threshold) script encodes and sets your offchain config, which includes the maximum gas price in wei. 1. To run this example, clone the repo and install its dependencies: ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```sh git clone https://github.com/smartcontractkit/smart-contract-examples.git && cd smart-contract-examples/automation-gas-threshold ``` ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```sh npm install ``` 2. Set the following variables: - `YOUR_RPC_URL`: The RPC URL for your provider (such as Alchemy or Infura) - `YOUR_PRIVATE_KEY`: Your wallet's private key - `YOUR_UPKEEP_ID`: The ID of the Automation upkeep you want to configure. - Within the `offchainConfig` variable, set your `maxGasPrice` in wei. Do not use quotation marks around the value you set for `maxGasPrice`. If this string is formatted incorrectly, the feature does not work. Here's an example of correct formatting: `{"maxGasPrice":2000000000}` 3. Run the script: ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```sh node index.js ``` ### [Remove the maximum gas price](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#remove-the-maximum-gas-price) To remove the maximum gas price from your upkeep, set the value of your `offchainConfig` back to `0x00`: - Encode this request with CBOR encoding. - Run `setUpkeepOffchainConfig` on the registry using your upkeep ID and the CBOR encoded value for `0x00`. If you're using [the gas threshold script](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold#run-the-script), set the `offchainConfig` variable in the script to `0`: ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```js // Change this value from `{"maxGasPrice":2000000000}` to `0`: const offchainConfig = 0 ``` ## [Create a new upkeep with a gas price threshold](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#create-a-new-upkeep-with-a-gas-price-threshold) To create a new upkeep with a gas threshold in place, you can [create a conditional upkeep or log trigger upkeep programmatically](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract#register-the-upkeep). **Note:** The [Chainlink Automation App](https://automation.chain.link/) does not yet support setting a gas price threshold when creating a new upkeep. You need to format and encode your offchain config before you set the `offchainConfig` parameter. You can adjust [the gas threshold script](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold#run-the-script) to get the encoded value for your offchain config and set that as the `offchainConfig` variable when [creating your new upkeep](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract#register-the-upkeep), or you can encode your config using the Solidity or Go examples below. ### [Format and encode your offchain config](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold\#format-and-encode-your-offchain-config) Currently, the only parameter that you can set in your upkeep's offchain config is `maxGasPrice`. You need to format your offchain config as a JSON object and CBOR encode it before you update it on the registry. 1. Format your offchain config as JSON. For example: `{"maxGasPrice": 100000000000}`. Use quotation marks only around the key, `"maxGasPrice"`. Do not use quotation marks around the value of the maximum gas price you are setting. 2. Encode the JSON object using CBOR encoding: SolidityGo ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; import { CBOR } from "@chainlink/contracts/src/v0.8/vendor/solidity-cborutils/v2.0.0/CBOR.sol" contract CBOREncoder { using CBOR for CBOR.CBORBuffer; // @notice encodes a max gas price to CBOR encoded bytes // @param maxGasPrice The max gas price // @return CBOR encoded bytes and the struct depth function encode(uint256 maxGasPrice, uint256 capacity) external pure returns (bytes memory, uint256) { CBOR.CBORBuffer memory buffer = CBOR.create(capacity); buffer.writeString("maxGasPrice"); buffer.writeUInt256(maxGasPrice); return (buffer.buf.buf, buffer.depth); } } ``` For Go, create a simple struct like this and encode it with the [cbor package](https://github.com/fxamacker/cbor). ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```go type UpkeepOffchainConfig struct { MaxGasPrice *big.Int `json:"maxGasPrice" cbor:"maxGasPrice"` } ``` ## What's next - [\> Register Custom Logic Upkeeps](https://docs.chain.link/chainlink-automation/guides/register-upkeep) - [\> Register Log Trigger Upkeeps](https://docs.chain.link/chainlink-automation/guides/log-trigger) - [\> Automation Architecture](https://docs.chain.link/chainlink-automation/concepts/automation-architecture) - [\> Billing and Costs](https://docs.chain.link/chainlink-automation/overview/automation-economics) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Updates [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Chainlink Automation Release Notes](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#overview) ## [2025-02-07 - Automation Expands to New Blockchains](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2025-02-07-automation-expands-to-new-blockchains) Chainlink Automation expands support to new blockchains: - Polygon zkEVM - Scroll Visit the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) page for more information. ## [2024-09-27 - Automation on Base mainnet](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-09-27-automation-on-base-mainnet) Chainlink Automation is live on [Base mainnet](https://docs.chain.link/chainlink-automation/overview/supported-networks#base-mainnet). ## [2024-09-09 - Native billing on Base Sepolia testnet for Automation](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-09-09-native-billing-on-base-sepolia-testnet-for-automation) Payment in native gas tokens and approved non-LINK tokens is now available for upkeeps on Automation v2.3 or later. Currently this capability is available on the Base Sepolia testnet. When you register a new upkeep, you can select whether you want to pay in LINK, the native gas token, or an approved non-LINK token for the chain that you are using. After you register the upkeep, the payment setting cannot be updated. See the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks#base-sepolia-testnet) page to find the contract and config information for Automation on Base Sepolia testnet. ## [2024-08-29 - Automation Log trigger upkeeps are generally available](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-08-29-automation-log-trigger-upkeeps-are-generally-available) [Log trigger upkeeps](https://docs.chain.link/chainlink-automation/guides/log-trigger) are now generally available. Learn more about [how Chainlink Automation processes logs](https://docs.chain.link/chainlink-automation/concepts/automation-concepts#maximum-logs-processed-for-log-trigger-upkeeps) for log trigger upkeeps. ## [2024-07-30 - Migrating upkeeps on paused registries](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-07-30-migrating-upkeeps-on-paused-registries) If you have any upkeeps that are not yet migrated to v2.1, using older registries that are paused, the only action you can take is either to migrate these upkeeps or to cancel them. Affected upkeeps will show a **Deprecated** label in the Chainlink Automation App. When you hover over the label, it displays a link you can click to begin the migration process. ## [2024-06-28 - Automation on Base Sepolia](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-06-28-automation-on-base-sepolia) Chainlink Automation is live on [Base Sepolia](https://docs.chain.link/chainlink-automation/overview/supported-networks#base-sepolia-testnet). ## [2024-06-24 - Deprecation of older Automation upkeeps](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-06-24-deprecation-of-older-automation-upkeeps) Existing upkeeps on versions earlier than v2.1 will stop being performed on August 29, 2024. [Migrate your older upkeeps](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2) to the latest version of Automation. Older Automation registrars through v2.0 have [already been deprecated](https://docs.chain.link/chainlink-automation/overview/automation-release-notes#2024-06-03---registrar-deprecation-through-v20), so you can't register new upkeeps on versions earlier than v2.1. ## [2024-06-06 - Automation on Gnosis](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-06-06-automation-on-gnosis) Chainlink Automation is live on [Gnosis](https://docs.chain.link/chainlink-automation/overview/supported-networks#gnosis-chain-xdai). ## [2024-06-03 - Registrar deprecation through v2.0](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-06-03-registrar-deprecation-through-v2-0) Older Automation [registrars](https://docs.chain.link/chainlink-automation/reference/automation-contracts#automationregistrarsol) for v1.0, v1.1, v1.2, v1.3, and v2.0 are deprecated on all [supported networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) (Ethereum, Avalanche, BSC, Polygon, Arbitrum and Optimism). You can no longer register new upkeeps using these older versions. Please [migrate your older upkeeps to Automation 2.1](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2) to ensure they remain operational as we start deprecating older versions. ## [2024-04-23 - Automation on Polygon Amoy](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-04-23-automation-on-polygon-amoy) Chainlink Automation is live on [Polygon Amoy](https://docs.chain.link/chainlink-automation/overview/supported-networks#amoy-testnet). ## [2024-04-13 - Polygon Mumbai support removed for Automation](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-04-13-polygon-mumbai-support-removed-for-automation) The Mumbai network has stopped producing blocks, so example code will not function on this network. Check again soon for updates about future testnet support on Polygon. ## [2024-03-07 - Automation StreamsLookup error handler](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-03-07-automation-streamslookup-error-handler) The [Automation StreamsLookup error handler](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler) is available to help you handle potential errors with StreamsLookup upkeeps. When you add the new [`checkErrorHandler`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#checkerrorhandler-function) function, you can define custom logic to handle some errors offchain and handle other errors onchain in `performUpkeep`. ## [2024-02-27 - Automation on Optimism Sepolia](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-02-27-automation-on-optimism-sepolia) Chainlink Automation is live on [Optimism Sepolia](https://docs.chain.link/chainlink-automation/overview/supported-networks#op-sepolia). ## [2024-02-27 - Automation debugging script](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2024-02-27-automation-debugging-script) The Chainlink [Automation debugging script](https://github.com/smartcontractkit/chainlink/blob/develop/core/scripts/chaincli/DEBUGGING.md) is available to help you debug and diagnose possible issues with registered upkeeps in Automation 2.1 registries. The script can debug custom logic upkeeps, log trigger upkeeps, and upkeeps that use `StreamsLookup`. ## [2023-12-07 - Automation on Base](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2023-12-07-automation-on-base) Chainlink Automation is live on [Base](https://docs.chain.link/chainlink-automation/overview/supported-networks#base). ## [2023-10-02 - Automation v2.0 release](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2023-10-02-automation-v2-0-release) Automation 2.0 is now live on Ethereum, Binance Smart Chain, Polygon, Avalanche, Arbitrum, and Optimism. Automation 2.0 features include: - **Verifiable compute**: The Automation DON now leverages a consensus mechanism, via Chainlink OCR3, to give you cryptographically verified compute. Save up to 90% of onchain gas costs by off-loading compute intensive tasks to the Automation DON. - **Log triggers**: Natively use log data in your smart contracts with [log triggers](https://docs.chain.link/chainlink-automation/guides/log-trigger). Unlock new connection possibilities. - **StreamsLookup**: Seamlessly access and use Chainlink's Low Latency Data in upkeeps via [StreamsLookup](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#streamslookupcompatibleinterface). Build like the best Derivative protocols. - **Forwarder**: A unique `msg.Sender` for your `performUpkeep` function so you can lock down sensitive upkeeps. Read more about the [forwarder](https://docs.chain.link/chainlink-automation/guides/forwarder). ## [2023-05-15 - Automation on Optimism](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#2023-05-15-automation-on-optimism) Chainlink Automation is live on [Optimism](https://docs.chain.link/chainlink-automation/overview/supported-networks#op). ## [Questions](https://docs.chain.link/chainlink-automation/overview/automation-release-notes\#questions) Ask questions in the [#automation channel](https://discord.com/channels/592041321326182401/821350860302581771) in our [Discord server](https://discord.gg/qj9qarT). ## What's next - [\> Register Time-Based Upkeeps](https://docs.chain.link/chainlink-automation/guides/job-scheduler) - [\> Register Custom Logic Upkeeps](https://docs.chain.link/chainlink-automation/guides/register-upkeep) - [\> Register Log Trigger Upkeeps](https://docs.chain.link/chainlink-automation/guides/log-trigger/) ## Get the latest Chainlink content straight to your inbox. Email Address ## Chainlink Automation Migration [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Migrate to v2.1](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#overview) Chainlink Automation 2.1 is a consensus-driven Automation solution that allows you to cut onchain gas costs by using cryptographically verified offchain compute. Automation 2.1 provides 10M gas worth of offchain compute, which is significantly more than previous versions. Additionally, Automation 2.1 provides increased reliability, performance, log trigger capability, and the ability to use `StreamsLookup` to retrieve Data Streams. You can migrate most upkeeps that use Automation version 1.2 and later in the [Chainlink Automation App](https://automation.chain.link/) or [in the block scanner](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#migrating-upkeeps-using-block-scanner). When you migrate upkeeps through the registry, you retain the Upkeep ID. Before you migrate, read the [migration checklist](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#migration-checklist) to maximize your benefits from Automation 2.1. For upkeeps on older registry versions 1.0 (Ethereum Mainnet), and 1.1 (BNB Mainnet and Polygon Mainnet), you must migrate manually by cancelling and re-registering your upkeep in the [Chainlink Automation App](https://automation.chain.link/). After you do this manual migration, future migrations will be easier because your new upkeeps will be eligible for the simpler migration process. ## [Migrating using the Chainlink Automation App](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#migrating-using-the-chainlink-automation-app) The Chainlink Automation App offers a streamlined migration process for upkeeps using registry versions 1.2 and later. To migrate upkeeps with older versions, follow the [manual migration process](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#migrating-older-upkeeps-manually) instead. 1. Navigate to the Chainlink Automation App, select the supported blockchain you're using, and connect your wallet. [Open the Chainlink Automation App](https://automation.chain.link/) ![](https://docs.chain.link/images/automation/v2-migration/landing-page.png) 2. To start migrating a specific upkeep, select the upkeep. In the **Details** page, expand the **Actions** menu and select **Migrate upkeep**. ![](https://docs.chain.link/images/automation/v2-migration/upkeep-details.png) If you have multiple upkeeps to migrate, start the migration using the link in your upkeeps dashboard. This link displays only if you have one or more upkeeps to migrate: ![](https://docs.chain.link/images/automation/v2-migration/1-bulk-migration-ui.png) 3. Follow the prompts to approve and confirm the transactions in your wallet to migrate your upkeeps. ![](https://docs.chain.link/images/automation/v2-migration/approve-migration.png) Upkeeps that are successfully migrated will show the following [transaction logs](https://testnet.bscscan.com/tx/0x85b3a147518719d3c9b1dd6b80e5f39d53d18c177ebc10d1bfb8890eaab8900a#eventlog): ![](https://docs.chain.link/images/automation/v2-migration/migration-txn.png) 4. If your upkeep restricts `msg.sender` to the previous registry address, [update your contract](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#update-permissions) to use the new forwarder address. After the migration is complete: - Your balance is transferred to the new registry automatically. - The new forwarder address becomes available. - Read the [migration checklist](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#migration-checklist) to understand further updates you might need to make in your contracts. ### [Migrating upkeeps on paused registries](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#migrating-upkeeps-on-paused-registries) If you have any upkeeps that are not yet migrated to v2.1, using older registries that are paused, the only action you can take is either to migrate these upkeeps or to cancel them. Affected upkeeps will show a **Deprecated** label in the Chainlink Automation App. When you hover over this label, it displays a link you can click to begin the migration process for the upkeep: ![](https://docs.chain.link/images/automation/v2-migration/ui-deprecation-label.png) ## [Migrating upkeeps using block scanner](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#migrating-upkeeps-using-block-scanner) To migrate one or more upkeeps using the scanner: 1. Navigate to the blockscanner for the desired chain to the Automation registry containing your upkeeps. You can find the registry address in the Chainlink Automation App under **Upkeep details**. 2. Under **Contract/Write contract** expand the `migrateUpkeeps` function. Enter a list of upkeep IDs. For example: `[99297446083125056953495436926707926113001699970195513612134585638496630959874,63026166139768298778579034679995711427803187091626268721992534052921779884688]`. 3. Enter the destination registry address, which is the latest registry address on this chain. You can find this address on the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) page, or at the top of the Chainlink Automation App with the desired chain selected. 4. Execute the `migrateUpkeeps` function and confirm the transaction. When this transaction is confirmed, your upkeeps will be migrated to the latest registry, and each upkeep will have a [unique forwarder](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#unique-forwarder) address. 5. If your upkeep restricts `msg.sender` to the previous registry address, [update your contract](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#update-permissions) to use the new forwarder address. After the migration is complete: - Your balance is transferred to the new registry automatically. - The new forwarder address becomes available. - Read the [migration checklist](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#migration-checklist) to understand further updates you might need to make in your contracts. ## [Migrating older upkeeps manually](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#migrating-older-upkeeps-manually) For upkeeps on registry versions 1.0 and 1.1, you must migrate upkeeps manually: 1. Navigate to the Upkeep in the Chainlink Automation App. [Open the Chainlink Automation App](https://automation.chain.link/) 2. In the **Details** section, navigate to the center **Upkeep** card. Copy the **Upkeep address** \- you need this for Step 5. 3. Expand the **Actions** menu and select **Cancel upkeep**. 4. Approve the transaction in your wallet. When this transaction is confirmed, you must wait 50 blocks before you can withdraw funds. 5. Return to the main [Chainlink Automation App](https://automation.chain.link/) landing page. Register a new upkeep, providing the **Upkeep address** of your old upkeep. 6. If your upkeep restricts `msg.sender` to the previous registry address, [update your contract](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#update-permissions) to use the new forwarder address. After migration, you have a new upkeep on Automation 2.1 with the same interface as your old upkeep. Future migrations are eligible for the simpler migration process. After the migration is complete: - Your balance is transferred to the new registry automatically. - The new forwarder address becomes available. - Read the [migration checklist](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#migration-checklist) to understand further updates you might need to make in your contracts. ## [Update permissions](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#update-permissions) Your new upkeep has a new [unique forwarder](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2#unique-forwarder) to increase security for your upkeep. This address will be the unique `msg.sender` for your upkeep. If your upkeep restricts `msg.sender` to the previous registry address, you must give permission to the forwarder address. Otherwise, Automation will no longer be able to execute your function. 1. The forwarder address becomes available after migrating your upkeep. You can find this in the Chainlink Automation App, within the upkeep's **Details** section: ![](https://docs.chain.link/images/automation/v2-migration/forwarder-address.png) 2. Update your contract to use the forwarder address by following the instructions on the [Forwarder](https://docs.chain.link/chainlink-automation/guides/forwarder) page. ### [Forwarders by upkeep type](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#forwarders-by-upkeep-type) This diagram shows the flow of different contracts that Automation 2.1 deploys for new and migrated upkeeps. Compared to custom logic and log trigger upkeeps, time-based upkeeps have an additional contract: ![](https://docs.chain.link/images/automation/v2-migration/v2-upkeeps-by-type.png) - For custom logic and log trigger upkeeps, the `msg.sender` in relation to your contract is the unique forwarder that Automation deploys when you migrate your upkeep. - For time-based upkeeps, Automation deploys a unique forwarder and a unique CRON upkeep contract. In this case, the CRON upkeep contract is the `msg.sender` in relation to your contract. ## [Migration checklist](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#migration-checklist) Before you migrate, be aware of several important changes listed here. ### [Unique forwarder](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#unique-forwarder) Automation 2.1 upkeeps are called from a unique forwarder per upkeep and not from the registry. If your upkeep restricts `msg.sender` to the previous registry address, you must update it to the newly created [forwarder address](https://docs.chain.link/chainlink-automation/guides/forwarder). The forwarder address becomes available only after the upkeep has been migrated. This forwarder address will remain constant in future migrations. ### [Update programmatic upkeeps](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#update-programmatic-upkeeps) Note that migration moves upkeeps from one registry to another. If you interact with your upkeep programmatically using Solidity or other interfaces, you must update your code to make sure that you are referencing the correct registry and registrar for your migrated upkeeps: - Update the registry and registrar addresses. - Ensure you use the latest version of the ABI for the registry and registrar. #### [Get the latest ABI](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#get-the-latest-abi) The latest ABI for Automation 2.1 is in the [@chainlink npm package](https://www.npmjs.com/package/@chainlink/contracts?activeTab=code): - Registry ABI: `@chainlink/contracts/abi/v0.8/IKeeperRegistryMaster.json` - Registrar ABI: `@chainlink/contracts/abi/v0.8/AutomationRegistrar2_1.json` After updating to the latest ABI, you will able to execute `registry.getForwarder(upkeepID)` to get the forwarder address in Solidity. #### [Check function signatures](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#check-function-signatures) If your contract makes function calls to the registry from your upkeep contract, follow the latest ABI. ### [Funding is moved with migration](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#funding-is-moved-with-migration) When you migrate, your LINK funding is moved from one registry to the next automatically. ### [Migration questions and feedback](https://docs.chain.link/chainlink-automation/guides/migrate-to-v2\#migration-questions-and-feedback) If you have questions or feedback, contact us in the [#automation channel](https://discord.com/channels/592041321326182401/821350860302581771) on the [Chainlink Discord server](https://discord.gg/qj9qarT). ## Get the latest Chainlink content straight to your inbox. Email Address ## StreamsLookup Error Handling [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Using the StreamsLookup error handler](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler\#overview) The Chainlink Automation StreamsLookup error handler provides insight into potential errors or edge cases in StreamsLookup upkeeps. The table below outlines a range of error codes and the behavior associated with the codes. Use the `checkErrorHandler` function to specify how you want to respond to the error codes. `checkErrorHandler` is simulated offchain and determines what action for Automation to take onchain in `performUpkeep`. ## [Error handler](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler\#error-handler) When Automation detects an event, it runs the `checkLog` function, which includes a [StreamsLookup revert](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#streamslookup-revert) custom error. The StreamsLookup revert enables your upkeep to fetch a report from Data Streams. If reports are fetched successfully, the [`checkCallback`](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#checkcallback-function) function is evaluated offchain. Otherwise, the `checkErrorHandler` function is evaluated offchain to determine what Automation should do next. Both of these functions have the same output types ( `bool upkeepNeeded, bytes memory performData`), which Automation uses to run `performUpkeep` onchain. The [example code](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler#example-code) also shows each function outlined in the diagram below: ![Error handler flow diagram](https://docs.chain.link/images/automation/streamslookup-errorhandler-horizontal.png) If the Automation network fails to get the requested reports, an error code is sent to the `checkErrorHandler` function in your contract. If your contract doesn't have the `checkErrorHandler` function, nothing will happen. If your contract has the `checkErrorHandler` function, it is evaluated offchain to determine what to do next. For example, you could intercept or ignore certain errors and decide not to run `performUpkeep` in those cases, in order to save time and gas. For other errors, you can execute an alternative path within `performUpkeep`, and the upkeep runs the custom logic you define in your `performUpkeep` function to handle those errors. 1. Add the `checkErrorHandler` function in your contract to specify how you want to handle [error codes](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler#error-codes). For example, you could decide to ignore any codes related to bad requests or incorrect input, without running `performUpkeep` onchain: ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity /** * @notice Determines the need for upkeep in response to an error from Data Streams. * @param errorCode The error code returned by the Data Streams lookup. * @param extraData Additional context or data related to the error condition. * @return upkeepNeeded Boolean indicating whether upkeep is needed based on the error. * @return performData Data to be used if upkeep is performed, encoded with success state and error context. */ function checkErrorHandler( uint errorCode, bytes calldata extraData ) external returns (bool upkeepNeeded, bytes memory performData) { // Add custom logic to handle errors offchain here bool _upkeepNeeded = true; bool reportSuccess = false; if (errorCode == 808400) { // Handle bad request errors code offchain. // In this example, no upkeep needed for bad request errors. _upkeepNeeded = false; } else { // Handle other errors as needed. } return (_upkeepNeeded, abi.encode(reportSuccess, abi.encode(errorCode, extraData))); } ``` 2. Define custom logic for the alternative path within `performUpkeep`, to handle any error codes you did not intercept offchain in `checkErrorHandler`: ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // function will be performed on-chain function performUpkeep(bytes calldata performData) external { // Decode incoming performData (bool reportSuccess, bytes memory payload) = abi.decode(performData, (bool, bytes)); if (reportSuccess) { // Decode the performData bytes passed in by CL Automation. // This contains the data returned by your implementation in checkCallback(). (bytes[] memory signedReports, bytes memory extraData) = abi.decode(payload, (bytes[], bytes)); // Logic to verify and decode report // ... } else { // Handle error condition (uint errorCode, bytes memory extraData) = abi.decode(payload, (uint, bytes)); // Custom logic to handle error codes } } ``` ### [Testing checkErrorHandler](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler\#testing-checkerrorhandler) `checkErrorHandler` is simulated offchain. When `upkeepNeeded` returns `true`, Automation runs `performUpkeep` onchain using the `performData` from `checkErrorHandler`. If the `checkErrorHandler` function itself reverts, `performUpkeep` does not run. If you need to force errors in StreamsLookup while testing, you can try the following methods: - Not specifying any `feedID` to force error code 808400 ( `ErrCodeStreamsBadRequest`) - Specifying an incorrect `feedID` to force error code 808401 ( `ErrCodeStreamsBadRequest`) - Specifying a future timestamp to force error code 808206 (where partial content is received) for both single `feedID` and bulk `feedID` requests - Specifying old timestamps for reports not available anymore yields either error code 808504 (no response) or 808600 (bad response), depending on which service calls the timeout request If your [StreamsLookup revert](https://docs.chain.link/chainlink-automation/reference/automation-interfaces#streamslookup-revert) function is defined incorrectly in your smart contracts, the nodes will not be able to decode it. ## [Error codes](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler\#error-codes) | Error code | Retries | Possible cause of error | | --- | --- | --- | | No error | N/A | No error | | ErrCodeStreamsBadRequest: 808400 | No | User requested 0 feeds | | User error, incorrect parameter input | | Issue with encoding http url (bad characters) | | ErrCodeStreamsUnauthorized: 808401 | No | Key access issue or incorrect feedID | | 808206 | Log trigger - after retries; Conditional immediately | Requested m reports but only received n (partial) | | 8085XX (e.g 808500) | Log trigger - after retries; Conditional immediately | No response | | ErrCodeStreamsBadResponse: 808600 | No | Error in reading body of returned response, but service is up | | ErrCodeStreamsTimeout: 808601 | No | No valid report is received for 10 seconds | | ErrCodeStreamsUnknownError: 808700 | No | Unknown | ## [Example code](https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler\#example-code) This example code includes the `revert StreamsLookup`, `checkCallback`, `checkErrorHandler` and `performUpkeep` functions. The full code example is available [here](https://github.com/smartcontractkit/documentation/blob/main/public/samples/DataStreams/StreamsUpkeepWithErrorHandler.sol). ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {StreamsLookupCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol"; import {ILogAutomation, Log} from "@chainlink/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol"; import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol"; import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol"; import {IERC20} from "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol"; /** * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */ // ===================== // INTERFACES // ===================== interface IFeeManager { /** * @notice Calculates the fee and reward associated with verifying a report, including discounts for subscribers. * This function assesses the fee and reward for report verification, applying a discount for recognized subscriber addresses. * @param subscriber The address attempting to verify the report. A discount is applied if this address * is recognized as a subscriber. * @param unverifiedReport The report data awaiting verification. The content of this report is used to * determine the base fee and reward, before considering subscriber discounts. * @param quoteAddress The payment token address used for quoting fees and rewards. * @return fee The fee assessed for verifying the report, with subscriber discounts applied where applicable. * @return reward The reward allocated to the caller for successfully verifying the report. * @return totalDiscount The total discount amount deducted from the fee for subscribers. */ function getFeeAndReward( address subscriber, bytes memory unverifiedReport, address quoteAddress ) external returns (Common.Asset memory, Common.Asset memory, uint256); function i_linkAddress() external view returns (address); function i_nativeAddress() external view returns (address); function i_rewardManager() external view returns (address); } interface IVerifierProxy { /** * @notice Verifies that the data encoded has been signed. * correctly by routing to the correct verifier, and bills the user if applicable. * @param payload The encoded data to be verified, including the signed * report. * @param parameterPayload Fee metadata for billing. In the current implementation, * this consists of the abi-encoded address of the ERC-20 token used for fees. * @return verifierResponse The encoded report from the verifier. */ function verify( bytes calldata payload, bytes calldata parameterPayload ) external payable returns (bytes memory verifierResponse); function s_feeManager() external view returns (IVerifierFeeManager); } // ========================== // CONTRACT IMPLEMENTATION // ========================== contract StreamsUpkeepWithErrorHandler is ILogAutomation, StreamsLookupCompatibleInterface { error InvalidReportVersion(uint16 version); // Thrown when an unsupported report version is provided to verifyReport. /** * @dev Represents a data report from a Data Streams feed for v3 schema (crypto streams). * The `price`, `bid`, and `ask` values are carried to either 8 or 18 decimal places, depending on the feed. * For more information, see https://docs.chain.link/data-streams/crypto-streams and https://docs.chain.link/data-streams/reference/report-schema */ struct ReportV3 { bytes32 feedId; // The feed ID the report has data for. uint32 validFromTimestamp; // Earliest timestamp for which price is applicable. uint32 observationsTimestamp; // Latest timestamp for which price is applicable. uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH). uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median price (8 or 18 decimals). int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals). int192 ask; // Simulated price impact of a sell order up to the X% depth of liquidity utilisation (8 or 18 decimals). } /** * @dev Represents a data report from a Data Streams feed for v4 schema (RWA feeds). * The `price` value is carried to either 8 or 18 decimal places, depending on the feed. * The `marketStatus` indicates whether the market is currently open. Possible values: `0` (`Unknown`), `1` (`Closed`), `2` (`Open`). * For more information, see https://docs.chain.link/data-streams/rwa-streams and https://docs.chain.link/data-streams/reference/report-schema-v4 */ struct ReportV4 { bytes32 feedId; // The feed ID the report has data for. uint32 validFromTimestamp; // Earliest timestamp for which price is applicable. uint32 observationsTimestamp; // Latest timestamp for which price is applicable. uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH). uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median benchmark price (8 or 18 decimals). uint32 marketStatus; // The DON's consensus on whether the market is currently open. } struct Quote { address quoteAddress; } event PriceUpdate(int192 indexed price); event ErrorTestLog(uint indexed errorCode); IVerifierProxy public verifier; address public FEE_ADDRESS; string public constant STRING_DATASTREAMS_FEEDLABEL = "feedIDs"; string public constant STRING_DATASTREAMS_QUERYLABEL = "timestamp"; uint256 public s_error; bool public s_isError; string[] public feedIds = [\ "0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782" // Ex. ETH/USD Feed ID\ ]; constructor(address _verifier) { verifier = IVerifierProxy(_verifier); //Arbitrum Sepolia: 0x2ff010debc1297f19579b4246cad07bd24f2488a } // This function uses revert to convey call information. // See https://eips.ethereum.org/EIPS/eip-3668#rationale for details. function checkLog( Log calldata log, bytes memory ) external returns (bool upkeepNeeded, bytes memory performData) { revert StreamsLookup( STRING_DATASTREAMS_FEEDLABEL, feedIds, STRING_DATASTREAMS_QUERYLABEL, log.timestamp, "" ); } /** * @dev This function is intended for off-chain simulation by Chainlink Automation to pass in the data reports fetched from Data Streams. * @param values The bytes array of data reports fetched from Data Streams. * @param extraData Contextual or additional data related to the feed lookup process. * @return upkeepNeeded Indicates that upkeep is needed to pass the data to the on-chain performUpkeep function. * @return performData Encoded data indicating success and including the original `values` and `extraData`, to be used in `performUpkeep`. */ function checkCallback( bytes[] calldata values, bytes calldata extraData ) external pure returns (bool upkeepNeeded, bytes memory) { bool reportSuccess = true; // Indicates successful data retrieval return (true, abi.encode(reportSuccess, abi.encode(values, extraData))); } /** * @notice Determines the need for upkeep in response to an error from Data Streams. * @dev This function serves as an example of how errors can be handled offchain. * @dev Developers can parameterize this logic as needed. * @dev All error codes are documented at: https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler#error-codes * @param errorCode The error code returned by the Data Streams lookup. * @param extraData Additional context or data related to the error condition. * @return upkeepNeeded Boolean indicating whether upkeep is needed based on the error. * @return performData Data to be used if upkeep is performed, encoded with success state and error context. */ function checkErrorHandler( uint errorCode, bytes calldata extraData ) external view returns (bool upkeepNeeded, bytes memory performData) { bool _upkeepNeeded = false; bool reportSuccess = false; if (errorCode == 0) { // If there is no error, proceed with the performUpkeep and // the report decoding/verification _upkeepNeeded = true; reportSuccess = true; } else if (errorCode == 808400 || errorCode == 808401) { // Mark upkeep as needed for bad requests (808400) and incorrect feed ID (808401) // to handle these specific errors onchain. _upkeepNeeded = true; // Note that reportSuccess remains false. } else { // For other error codes, decide not to perform upkeep. // This is the default behavior, explicitly noted for clarity in this example. _upkeepNeeded = false; reportSuccess = false; } return ( _upkeepNeeded, abi.encode(reportSuccess, abi.encode(errorCode, extraData)) ); } function performUpkeep(bytes calldata performData) external { // Decode incoming performData (bool reportSuccess, bytes memory payload) = abi.decode( performData, (bool, bytes) ); if (reportSuccess) { // Decode the performData bytes passed in by CL Automation. // This contains the data returned by your implementation in checkCallback(). (bytes[] memory signedReports, bytes memory extraData) = abi.decode( payload, (bytes[], bytes) ); // Logic to verify and decode report bytes memory unverifiedReport = signedReports[0]; (, bytes memory reportData) = abi.decode( unverifiedReport, (bytes32[3], bytes) ); // Extract report version from reportData uint16 reportVersion = (uint16(uint8(reportData[0])) << 8) | uint16(uint8(reportData[1])); // Validate report version if (reportVersion != 3 && reportVersion != 4) { revert InvalidReportVersion(uint8(reportVersion)); } // Report verification fees IFeeManager feeManager = IFeeManager( address(verifier.s_feeManager()) ); IRewardManager rewardManager = IRewardManager( address(feeManager.i_rewardManager()) ); address feeTokenAddress = feeManager.i_linkAddress(); (Common.Asset memory fee, , ) = feeManager.getFeeAndReward( address(this), reportData, feeTokenAddress ); // Approve rewardManager to spend this contract's balance in fees IERC20(feeTokenAddress).approve(address(rewardManager), fee.amount); // Verify the report bytes memory verifiedReportData = verifier.verify( unverifiedReport, abi.encode(feeTokenAddress) ); // Decode verified report data into the appropriate Report struct based on reportVersion if (reportVersion == 3) { // v3 report schema ReportV3 memory verifiedReport = abi.decode( verifiedReportData, (ReportV3) ); // Log price from report emit PriceUpdate(verifiedReport.price); } else if (reportVersion == 4) { // v4 report schema ReportV4 memory verifiedReport = abi.decode( verifiedReportData, (ReportV4) ); // Log price from report emit PriceUpdate(verifiedReport.price); } } else { // Handle error condition (uint errorCode, bytes memory extraData) = abi.decode( payload, (uint, bytes) ); // Custom logic to handle error codes s_error = errorCode; s_isError = true; } } fallback() external payable {} receive() external payable {} } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/StreamsUpkeepWithErrorHandler.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) ## What's next - [\> Troubleshoot and Debug Upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors) - [\> Automation Interfaces](https://docs.chain.link/chainlink-automation/reference/automation-interfaces) - [\> Automation Contracts](https://docs.chain.link/chainlink-automation/reference/automation-contracts) ## Get the latest Chainlink content straight to your inbox. Email Address ## Register Upkeep in Contract [iframe](https://www.googletagmanager.com/ns.html?id=GTM-N6DQ47T) Chainlink CCIP is now officially live on Solana. [View lanes and tokens.](https://docs.chain.link/ccip/directory/mainnet/chain/solana-mainnet?utm_medium=referral&utm_source=chainlink-docs&utm_campaign=solana-ccip) On this page # [Register Upkeeps Programmatically](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#overview) This guide explains how to register an upkeep from within your smart contract, also called programmatic upkeep creation. Your contract can then interact with it via the registry to get its balance, fund it, edit it, or cancel it using the `upkeepID`. ## [Before you begin](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#before-you-begin) Before beginning this process, complete the following tasks: 1. Ensure the smart contract you want to automate is [Automation Compatible](https://docs.chain.link/chainlink-automation/guides/compatible-contracts). To learn more about the contracts Chainlink Automation uses, click [here](https://docs.chain.link/chainlink-automation/reference/automation-contracts). 2. Ensure you have sufficient LINK in the contract that will be registering the Upkeep. Use [faucets.chain.link](https://faucets.chain.link/) to get testnet LINK. 3. Ensure you have the addresses for the LINK token you are using, and the correct registry/registrar. You can find these values on the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks) page. _Note_: You can retrieve the LINK token address by calling the function `getLinkAddress` on the registry . 4. Use variables for the registry and registrar addresses that your admin can change as new versions of Chainlink Automation are released. 5. The interface for LINK and Registrar for registration, interface for Registry for subsequent actions 6. Interface is like the API specification of interacting with the contract. ## [Register the upkeep](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#register-the-upkeep) Programmatically registering an upkeep happens in two steps: 1. Call the LINK token to give allowance to the Automation registrar for the amount of LINK you will fund your upkeep with at registration time, e.g. Pizza code to do 2. Call `registerUpkeep` on the Registrar contract using the `RegistrationParams` struct. You will receive the `upkeepID` if successful. | Var type | Var Name | Example value | Description | | --- | --- | --- | --- | | String | name | "Test upkeep" | Name of upkeep that will be displayed in the UI. | | bytes | encryptedEmail | 0x | Can leave blank. If registering via UI we will encrypt email and store it here. | | address | upkeepContract | | Address of your Automation-compatible contract | | uint32 | gasLimit | 500000 | The maximum gas limit that will be used for your txns. Rather over-estimate gas since you only pay for what you use, while too low gas might mean your upkeep doesn't perform. Trade-off is higher gas means higher minimum funding requirement. | | address | adminAddress | | The address that will have admin rights for this upkeep. Use your wallet address, unless you want to make another wallet the admin. | | uint8 | triggerType | 0 or 1 | 0 is Conditional upkeep, 1 is Log trigger upkeep | | bytes | checkData | 0x | checkData is a static input that you can specify now which will be sent into your checkUpkeep or checkLog, see interface. | | bytes | triggerConfig | 0x | The configuration for your upkeep. 0x for conditional upkeeps, or see next section for log triggers. | | bytes | offchainConfig | 0x | Leave as 0x, or use this field to set a [gas price threshold](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold) for your upkeep. Must be a JSON object and CBOR encoded - see [more details and examples on formatting](https://docs.chain.link/chainlink-automation/guides/gas-price-threshold#format-and-encode-your-offchain-config). | | uint96 | amount | 1000000000000000000 | Ensure this is less than or equal to the allowance just given, and needs to be in WEI. | ## [Upkeep registration parameters and examples](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#upkeep-registration-parameters-and-examples) Depending on the trigger you are using, the `triggerConfig` will be different. Browse the triggers below to understand how to set up `triggerConfig`. ### [Custom logic upkeeps](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#custom-logic-upkeeps) #### [Parameters](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#parameters) For upkeeps with triggers using onchain state only, the following parameters are needed: #### [Code sample](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#code-sample) ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"; /** * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */ struct RegistrationParams { string name; bytes encryptedEmail; address upkeepContract; uint32 gasLimit; address adminAddress; uint8 triggerType; bytes checkData; bytes triggerConfig; bytes offchainConfig; uint96 amount; } /** * string name = "test upkeep"; * bytes encryptedEmail = 0x; * address upkeepContract = 0x...; * uint32 gasLimit = 500000; * address adminAddress = 0x....; * uint8 triggerType = 0; * bytes checkData = 0x; * bytes triggerConfig = 0x; * bytes offchainConfig = 0x; * uint96 amount = 1000000000000000000; */ interface AutomationRegistrarInterface { function registerUpkeep( RegistrationParams calldata requestParams ) external returns (uint256); } contract UpkeepIDConditionalExample { LinkTokenInterface public immutable i_link; AutomationRegistrarInterface public immutable i_registrar; constructor( LinkTokenInterface link, AutomationRegistrarInterface registrar ) { i_link = link; i_registrar = registrar; } function registerAndPredictID(RegistrationParams memory params) public { // LINK must be approved for transfer - this can be done every time or once // with an infinite approval i_link.approve(address(i_registrar), params.amount); uint256 upkeepID = i_registrar.registerUpkeep(params); if (upkeepID != 0) { // DEV - Use the upkeepID however you see fit } else { revert("auto-approve disabled"); } } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/Automation/UpkeepIDConditionalExample.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) ### [Log trigger upkeeps](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#log-trigger-upkeeps) #### [Parameters](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#parameters-1) For upkeeps with triggers using emitted logs, the following parameters are needed: ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity struct LogTriggerConfig { address contractAddress; // must have address that will be emitting the log uint8 filterSelector; // must have filtserSelector, denoting which topics apply to filter ex 000, 101, 111...only last 3 bits apply bytes32 topic0; // must have signature of the emitted event bytes32 topic1; // optional filter on indexed topic 1 bytes32 topic2; // optional filter on indexed topic 2 bytes32 topic3; // optional filter on indexed topic 3 } ``` where filterSelector is a bitmask mapping and value is set depending on the selection of filters | filterSelector | Topic 1 | Topic 2 | Topic 3 | | --- | --- | --- | --- | | 0 | Empty | Empty | Empty | | 1 | Filter | Empty | Empty | | 2 | Empty | Filter | Empty | | 3 | Filter | Filter | Empty | | 4 | Empty | Empty | Filter | | 5 | Filter | Empty | Filter | | 6 | Empty | Filter | Filter | | 7 | Filter | Filter | Filter | #### [Code sample](https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract\#code-sample-1) ![copy to clipboard](https://docs.chain.link/assets/icons/copyIcon.svg) ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"; /** * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */ struct RegistrationParams { string name; bytes encryptedEmail; address upkeepContract; uint32 gasLimit; address adminAddress; uint8 triggerType; bytes checkData; bytes triggerConfig; bytes offchainConfig; uint96 amount; } struct LogTriggerConfig { address contractAddress; uint8 filterSelector; bytes32 topic0; bytes32 topic1; bytes32 topic2; bytes32 topic3; } /** * Log trigger details * address contractAddress = 0x...; // e.g. 0x2938ff7cAB3115f768397602EA1A1a0Aa20Ac42f * uint8 filterSelector = 1; // see filterSelector * bytes32 topic0 = 0x...; // e.g. 0x74500d2e71ee75a8a83dcc87f7316a89404a0d0ac0c725e80c956dbf16fb8133 for event called bump * bytes32 topic1 = 0x...; // e.g. bytes32 of address 0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637 * bytes32 topic2 = 0x; // empty so 0x * bytes32 topic3 = 0x; // empty so 0x * * Upkeep details * string name = "test upkeep"; * bytes encryptedEmail = 0x; * address upkeepContract = 0x...; * uint32 gasLimit = 500000; * address adminAddress = 0x....; * uint8 triggerType = 1; * bytes checkData = 0x; * bytes triggerConfig = abi.encode(address contractAddress, uint8 filterSelector,bytes32 topic0,bytes32 topic1,bytes32 topic2, bytes32 topic3); * bytes offchainConfig = 0x; * uint96 amount = 1000000000000000000; */ interface AutomationRegistrarInterface { function registerUpkeep( RegistrationParams calldata requestParams ) external returns (uint256); } contract UpkeepIDlogTriggerExample { LinkTokenInterface public immutable i_link; AutomationRegistrarInterface public immutable i_registrar; constructor( LinkTokenInterface link, AutomationRegistrarInterface registrar ) { i_link = link; i_registrar = registrar; } function registerAndPredictID(RegistrationParams memory params) public { // LINK must be approved for transfer - this can be done every time or once // with an infinite approval i_link.approve(address(i_registrar), params.amount); uint256 upkeepID = i_registrar.registerUpkeep(params); if (upkeepID != 0) { // DEV - Use the upkeepID however you see fit } else { revert("auto-approve disabled"); } } } ``` [Open in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/Automation/UpkeepIDlogTriggerExample.sol&autoCompile=true) [What is Remix?](https://docs.chain.link/getting-started/conceptual-overview#what-is-remix) ## What's next - [\> Create Automation-Compatible Contracts](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) - [\> Troubleshoot and Debug Upkeeps](https://docs.chain.link/chainlink-automation/reference/debugging-errors) ## Get the latest Chainlink content straight to your inbox. Email Address