Register Upkeeps Programmatically
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
Before beginning this process, complete the following tasks:
-
Ensure the smart contract you want to automate is Automation Compatible. To learn more about the contracts Chainlink Automation uses, click here.
-
Ensure you have sufficient LINK in the contract that will be registering the Upkeep. Use faucets.chain.link to get testnet LINK.
-
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 page. Note: You can retrieve the LINK token address by calling the function
getLinkAddress
on the registry . -
Use variables for the registry and registrar addresses that your admin can change as new versions of Chainlink Automation are released.
-
The interface for LINK and Registrar for registration, interface for Registry for subsequent actions
-
Interface is like the API specification of interacting with the contract.
Register the upkeep
Programmatically registering an upkeep happens in two steps:
- 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
- Call
registerUpkeep
on the Registrar contract using theRegistrationParams
struct. You will receive theupkeepID
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 for your upkeep. Must be a JSON object and CBOR encoded - see more details and examples on formatting. |
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
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
Parameters
For upkeeps with triggers using onchain state only, the following parameters are needed:
Code sample
// 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");
}
}
}
Log trigger upkeeps
Parameters
For upkeeps with triggers using emitted logs, the following parameters are needed:
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
// 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");
}
}
}