Source Code
Overview
ETH Balance
Token Holdings
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 33 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Cancel | 8479298 | 6 days ago | IN | 0 ETH | 0.00000735 | ||||
Cancel | 8479297 | 6 days ago | IN | 0 ETH | 0.00000735 | ||||
Cancel | 8479294 | 6 days ago | IN | 0 ETH | 0.00000768 | ||||
Set Auto Approve... | 8469919 | 7 days ago | IN | 0 ETH | 0.00000156 | ||||
Set Auto Approve... | 8469914 | 7 days ago | IN | 0 ETH | 0.00000151 | ||||
Cancel | 8469749 | 7 days ago | IN | 0 ETH | 0.00000211 | ||||
Cancel | 8400313 | 14 days ago | IN | 0 ETH | 0.00000159 | ||||
Cancel | 8400310 | 14 days ago | IN | 0 ETH | 0.00000159 | ||||
Cancel | 8368184 | 17 days ago | IN | 0 ETH | 0.00000382 | ||||
Cancel | 8273562 | 25 days ago | IN | 0 ETH | 0.00000265 | ||||
Cancel | 8262390 | 26 days ago | IN | 0 ETH | 0.00000225 | ||||
Cancel | 8252458 | 27 days ago | IN | 0 ETH | 0.00001154 | ||||
Cancel | 8252455 | 27 days ago | IN | 0 ETH | 0.00001263 | ||||
Transfer Ownersh... | 7480223 | 102 days ago | IN | 0 ETH | 0.00000343 | ||||
Set Trigger Conf... | 7480219 | 102 days ago | IN | 0 ETH | 0.00003002 | ||||
Set Trigger Conf... | 7480219 | 102 days ago | IN | 0 ETH | 0.00003281 | ||||
Set Config | 7480218 | 102 days ago | IN | 0 ETH | 0.00003626 | ||||
Set Auto Approve... | 7472216 | 103 days ago | IN | 0 ETH | 0.00000882 | ||||
Set Trigger Conf... | 7381657 | 113 days ago | IN | 0 ETH | 0.00000124 | ||||
Set Trigger Conf... | 7381657 | 113 days ago | IN | 0 ETH | 0.00000124 | ||||
Set Config | 7381656 | 113 days ago | IN | 0 ETH | 0.00000148 | ||||
Set Trigger Conf... | 7319528 | 120 days ago | IN | 0 ETH | 0.00118285 | ||||
Set Trigger Conf... | 7319528 | 120 days ago | IN | 0 ETH | 0.00118285 | ||||
Set Config | 7319528 | 120 days ago | IN | 0 ETH | 0.00144136 | ||||
Register Upkeep | 7319365 | 120 days ago | IN | 0 ETH | 0.02021271 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
8518806 | 2 days ago | 0 ETH | ||||
8518806 | 2 days ago | 0 ETH | ||||
8518790 | 2 days ago | 0 ETH | ||||
8518790 | 2 days ago | 0 ETH | ||||
8499904 | 4 days ago | 0 ETH | ||||
8499904 | 4 days ago | 0 ETH | ||||
8499904 | 4 days ago | 0 ETH | ||||
8499904 | 4 days ago | 0 ETH | ||||
8499830 | 4 days ago | 0 ETH | ||||
8499830 | 4 days ago | 0 ETH | ||||
8499830 | 4 days ago | 0 ETH | ||||
8499830 | 4 days ago | 0 ETH | ||||
8499824 | 4 days ago | 0 ETH | ||||
8499824 | 4 days ago | 0 ETH | ||||
8499824 | 4 days ago | 0 ETH | ||||
8499824 | 4 days ago | 0 ETH | ||||
8499433 | 4 days ago | 0 ETH | ||||
8499433 | 4 days ago | 0 ETH | ||||
8499433 | 4 days ago | 0 ETH | ||||
8499433 | 4 days ago | 0 ETH | ||||
8499421 | 4 days ago | 0 ETH | ||||
8499421 | 4 days ago | 0 ETH | ||||
8499421 | 4 days ago | 0 ETH | ||||
8499421 | 4 days ago | 0 ETH | ||||
8499340 | 4 days ago | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xeec54458...d30883c9F The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
AutomationRegistrar2_3
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; import {IAutomationRegistryMaster2_3} from "../interfaces/v2_3/IAutomationRegistryMaster2_3.sol"; import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol"; import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IWrappedNative} from "../interfaces/v2_3/IWrappedNative.sol"; import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @notice Contract to accept requests for upkeep registrations * @dev There are 2 registration workflows in this contract * Flow 1. auto approve OFF / manual registration - UI calls `register` function on this contract, this contract owner at a later time then manually * calls `approve` to register upkeep and emit events to inform UI and others interested. * Flow 2. auto approve ON / real time registration - UI calls `register` function as before, which calls the `registerUpkeep` function directly on * keeper registry and then emits approved event to finish the flow automatically without manual intervention. * The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not. * they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations. */ contract AutomationRegistrar2_3 is TypeAndVersionInterface, ConfirmedOwner, IERC677Receiver { using SafeERC20 for IERC20; /** * DISABLED: No auto approvals, all new upkeeps should be approved manually. * ENABLED_SENDER_ALLOWLIST: Auto approvals for allowed senders subject to max allowed. Manual for rest. * ENABLED_ALL: Auto approvals for all new upkeeps subject to max allowed. */ enum AutoApproveType { DISABLED, ENABLED_SENDER_ALLOWLIST, ENABLED_ALL } /** * @notice versions: * - KeeperRegistrar 2.3.0: Update for compatability with registry 2.3.0 * Add native billing and ERC20 billing support * - KeeperRegistrar 2.1.0: Update for compatability with registry 2.1.0 * Add auto approval levels by type * - KeeperRegistrar 2.0.0: Remove source from register * Breaks our example of "Register an Upkeep using your own deployed contract" * - KeeperRegistrar 1.1.0: Add functionality for sender allowlist in auto approve * : Remove rate limit and add max allowed for auto approve * - KeeperRegistrar 1.0.0: initial release */ string public constant override typeAndVersion = "AutomationRegistrar 2.3.0"; /** * @notice TriggerRegistrationStorage stores the auto-approval levels for upkeeps by type * @member autoApproveType the auto approval setting (see enum) * @member autoApproveMaxAllowed the max number of upkeeps that can be auto approved of this type * @member approvedCount the count of upkeeps auto approved of this type */ struct TriggerRegistrationStorage { AutoApproveType autoApproveType; uint32 autoApproveMaxAllowed; uint32 approvedCount; } /** * @notice InitialTriggerConfig configures the auto-approval levels for upkeeps by trigger type * @dev this struct is only used in the constructor to set the initial values for various trigger configs * @member triggerType the upkeep type to configure * @member autoApproveType the auto approval setting (see enum) * @member autoApproveMaxAllowed the max number of upkeeps that can be auto approved of this type */ // solhint-disable-next-line gas-struct-packing struct InitialTriggerConfig { uint8 triggerType; AutoApproveType autoApproveType; uint32 autoApproveMaxAllowed; } struct PendingRequest { address admin; uint96 balance; IERC20 billingToken; } /** * @member upkeepContract address to perform upkeep on * @member amount quantity of billing token upkeep is funded with (specified in the billing token's decimals) * @member adminAddress address to cancel upkeep and withdraw remaining funds * @member gasLimit amount of gas to provide the target contract when performing upkeep * @member triggerType the type of trigger for the upkeep * @member billingToken the token to pay with * @member name string of the upkeep to be registered * @member encryptedEmail email address of upkeep contact * @member checkData data passed to the contract when checking for upkeep * @member triggerConfig the config for the trigger * @member offchainConfig offchainConfig for upkeep in bytes */ struct RegistrationParams { address upkeepContract; uint96 amount; // 1 word full address adminAddress; uint32 gasLimit; uint8 triggerType; // 7 bytes left in 2nd word IERC20 billingToken; // 12 bytes left in 3rd word string name; bytes encryptedEmail; bytes checkData; bytes triggerConfig; bytes offchainConfig; } LinkTokenInterface public immutable i_LINK; IWrappedNative public immutable i_WRAPPED_NATIVE_TOKEN; IAutomationRegistryMaster2_3 private s_registry; // Only applicable if trigger config is set to ENABLED_SENDER_ALLOWLIST mapping(address => bool) private s_autoApproveAllowedSenders; mapping(IERC20 => uint256) private s_minRegistrationAmounts; mapping(bytes32 => PendingRequest) private s_pendingRequests; mapping(uint8 => TriggerRegistrationStorage) private s_triggerRegistrations; event RegistrationRequested( bytes32 indexed hash, string name, bytes encryptedEmail, address indexed upkeepContract, uint32 gasLimit, address adminAddress, uint8 triggerType, bytes triggerConfig, bytes offchainConfig, bytes checkData, uint96 amount, IERC20 billingToken ); event RegistrationApproved(bytes32 indexed hash, string displayName, uint256 indexed upkeepId); event RegistrationRejected(bytes32 indexed hash); event AutoApproveAllowedSenderSet(address indexed senderAddress, bool allowed); event ConfigChanged(); event TriggerConfigSet(uint8 triggerType, AutoApproveType autoApproveType, uint32 autoApproveMaxAllowed); error HashMismatch(); error InsufficientPayment(); error InvalidAdminAddress(); error InvalidBillingToken(); error InvalidDataLength(); error TransferFailed(address to); error DuplicateEntry(); error OnlyAdminOrOwner(); error OnlyLink(); error RequestNotFound(); /** * @param LINKAddress Address of Link token * @param registry keeper registry address * @param triggerConfigs the initial config for individual triggers * @param billingTokens the tokens allowed for billing * @param minRegistrationFees the minimum amount for registering with each billing token * @param wrappedNativeToken wrapped native token */ constructor( address LINKAddress, IAutomationRegistryMaster2_3 registry, InitialTriggerConfig[] memory triggerConfigs, IERC20[] memory billingTokens, uint256[] memory minRegistrationFees, IWrappedNative wrappedNativeToken ) ConfirmedOwner(msg.sender) { i_LINK = LinkTokenInterface(LINKAddress); i_WRAPPED_NATIVE_TOKEN = wrappedNativeToken; setConfig(registry, billingTokens, minRegistrationFees); for (uint256 idx = 0; idx < triggerConfigs.length; idx++) { setTriggerConfig( triggerConfigs[idx].triggerType, triggerConfigs[idx].autoApproveType, triggerConfigs[idx].autoApproveMaxAllowed ); } } //EXTERNAL /** * @notice Allows external users to register upkeeps; assumes amount is approved for transfer by the contract * @param requestParams struct of all possible registration parameters */ function registerUpkeep(RegistrationParams memory requestParams) external payable returns (uint256) { if (requestParams.billingToken == IERC20(i_WRAPPED_NATIVE_TOKEN) && msg.value != 0) { requestParams.amount = SafeCast.toUint96(msg.value); // wrap and send native payment i_WRAPPED_NATIVE_TOKEN.deposit{value: msg.value}(); } else { // send ERC20 payment, including wrapped native token requestParams.billingToken.safeTransferFrom(msg.sender, address(this), requestParams.amount); } return _register(requestParams, msg.sender); } /** * @dev register upkeep on AutomationRegistry contract and emit RegistrationApproved event * @param requestParams struct of all possible registration parameters */ function approve(RegistrationParams calldata requestParams) external onlyOwner { bytes32 hash = keccak256(abi.encode(requestParams)); PendingRequest memory request = s_pendingRequests[hash]; if (request.admin == address(0)) { revert RequestNotFound(); } delete s_pendingRequests[hash]; _approve(requestParams, hash); } /** * @notice cancel will remove a registration request from the pending request queue and return the refunds to the request.admin * @param hash the request hash */ function cancel(bytes32 hash) external { PendingRequest memory request = s_pendingRequests[hash]; if (!(msg.sender == request.admin || msg.sender == owner())) { revert OnlyAdminOrOwner(); } if (request.admin == address(0)) { revert RequestNotFound(); } delete s_pendingRequests[hash]; request.billingToken.safeTransfer(request.admin, request.balance); emit RegistrationRejected(hash); } /** * @notice owner calls this function to set contract config * @param registry new keeper registry address * @param billingTokens the billing tokens that this registrar supports (registy must also support these) * @param minBalances minimum balances that users must supply to register with the corresponding billing token */ function setConfig( IAutomationRegistryMaster2_3 registry, IERC20[] memory billingTokens, uint256[] memory minBalances ) public onlyOwner { if (billingTokens.length != minBalances.length) revert InvalidDataLength(); s_registry = registry; for (uint256 i = 0; i < billingTokens.length; i++) { s_minRegistrationAmounts[billingTokens[i]] = minBalances[i]; } emit ConfigChanged(); } /** * @notice owner calls to set the config for this upkeep type * @param triggerType the upkeep type to configure * @param autoApproveType the auto approval setting (see enum) * @param autoApproveMaxAllowed the max number of upkeeps that can be auto approved of this type */ function setTriggerConfig( uint8 triggerType, AutoApproveType autoApproveType, uint32 autoApproveMaxAllowed ) public onlyOwner { s_triggerRegistrations[triggerType].autoApproveType = autoApproveType; s_triggerRegistrations[triggerType].autoApproveMaxAllowed = autoApproveMaxAllowed; emit TriggerConfigSet(triggerType, autoApproveType, autoApproveMaxAllowed); } /** * @notice owner calls this function to set allowlist status for senderAddress * @param senderAddress senderAddress to set the allowlist status for * @param allowed true if senderAddress needs to be added to allowlist, false if needs to be removed */ function setAutoApproveAllowedSender(address senderAddress, bool allowed) external onlyOwner { s_autoApproveAllowedSenders[senderAddress] = allowed; emit AutoApproveAllowedSenderSet(senderAddress, allowed); } /** * @notice read the allowlist status of senderAddress * @param senderAddress address to read the allowlist status for */ function getAutoApproveAllowedSender(address senderAddress) external view returns (bool) { return s_autoApproveAllowedSenders[senderAddress]; } /** * @notice gets the registry that this registrar is pointed to */ function getRegistry() external view returns (IAutomationRegistryMaster2_3) { return s_registry; } /** * @notice get the minimum registration fee for a particular billing token */ function getMinimumRegistrationAmount(IERC20 billingToken) external view returns (uint256) { return s_minRegistrationAmounts[billingToken]; } /** * @notice read the config for this upkeep type * @param triggerType upkeep type to read config for */ function getTriggerRegistrationDetails(uint8 triggerType) external view returns (TriggerRegistrationStorage memory) { return s_triggerRegistrations[triggerType]; } /** * @notice gets the admin address and the current balance of a registration request */ function getPendingRequest(bytes32 hash) external view returns (address, uint96) { PendingRequest memory request = s_pendingRequests[hash]; return (request.admin, request.balance); } /** * @notice Called when LINK is sent to the contract via `transferAndCall` * @param sender Address of the sender transfering LINK * @param amount Amount of LINK sent (specified in Juels) * @param data Payload of the transaction */ function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external override { if (msg.sender != address(i_LINK)) revert OnlyLink(); RegistrationParams memory params = abi.decode(data, (RegistrationParams)); if (address(params.billingToken) != address(i_LINK)) revert OnlyLink(); params.amount = uint96(amount); // ignore whatever is sent in registration params, use actual value; casting safe because max supply LINK < 2^96 _register(params, sender); } // ================================================================ // | PRIVATE | // ================================================================ /** * @dev verify registration request and emit RegistrationRequested event * @dev we don't allow multiple duplicate registrations by adding to the original registration's balance * users can cancel and re-register if they want to update the registration */ function _register(RegistrationParams memory params, address sender) private returns (uint256) { if (params.amount < s_minRegistrationAmounts[params.billingToken]) { revert InsufficientPayment(); } if (params.adminAddress == address(0)) { revert InvalidAdminAddress(); } if (!s_registry.supportsBillingToken(address(params.billingToken))) { revert InvalidBillingToken(); } bytes32 hash = keccak256(abi.encode(params)); if (s_pendingRequests[hash].admin != address(0)) { revert DuplicateEntry(); } emit RegistrationRequested( hash, params.name, params.encryptedEmail, params.upkeepContract, params.gasLimit, params.adminAddress, params.triggerType, params.triggerConfig, params.offchainConfig, params.checkData, params.amount, params.billingToken ); uint256 upkeepId; if (_shouldAutoApprove(s_triggerRegistrations[params.triggerType], sender)) { s_triggerRegistrations[params.triggerType].approvedCount++; upkeepId = _approve(params, hash); } else { s_pendingRequests[hash] = PendingRequest({ admin: params.adminAddress, balance: params.amount, billingToken: params.billingToken }); } return upkeepId; } /** * @dev register upkeep on AutomationRegistry contract and emit RegistrationApproved event * @dev safeApprove is deprecated and removed from the latest (v5) OZ release, Use safeIncreaseAllowance when we upgrade OZ (we are on v4.8) * @dev we stick to the safeApprove because of the older version (v4.8) of safeIncreaseAllowance can't handle USDT correctly, but newer version can */ function _approve(RegistrationParams memory params, bytes32 hash) private returns (uint256) { IAutomationRegistryMaster2_3 registry = s_registry; uint256 upkeepId = registry.registerUpkeep( params.upkeepContract, params.gasLimit, params.adminAddress, params.triggerType, address(params.billingToken), // have to cast as address because master interface doesn't use contract types params.checkData, params.triggerConfig, params.offchainConfig ); if (address(params.billingToken) == address(i_LINK)) { bool success = i_LINK.transferAndCall(address(registry), params.amount, abi.encode(upkeepId)); if (!success) { revert TransferFailed(address(registry)); } } else { params.billingToken.safeApprove(address(registry), params.amount); registry.addFunds(upkeepId, params.amount); } emit RegistrationApproved(hash, params.name, upkeepId); return upkeepId; } /** * @dev verify sender allowlist if needed and check max limit */ function _shouldAutoApprove(TriggerRegistrationStorage memory config, address sender) private view returns (bool) { if (config.autoApproveType == AutoApproveType.DISABLED) { return false; } if (config.autoApproveType == AutoApproveType.ENABLED_SENDER_ALLOWLIST && (!s_autoApproveAllowedSenders[sender])) { return false; } if (config.approvedCount < config.autoApproveMaxAllowed) { return true; } return false; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.16; import {IAutomationRegistryConsumer} from "./interfaces/IAutomationRegistryConsumer.sol"; uint256 constant PERFORM_GAS_CUSHION = 5_000; /** * @title AutomationForwarder is a relayer that sits between the registry and the customer's target contract * @dev The purpose of the forwarder is to give customers a consistent address to authorize against, * which stays consistent between migrations. The Forwarder also exposes the registry address, so that users who * want to programatically interact with the registry (ie top up funds) can do so. */ contract AutomationForwarder { /// @notice the user's target contract address address private immutable i_target; /// @notice the shared logic address address private immutable i_logic; IAutomationRegistryConsumer private s_registry; constructor(address target, address registry, address logic) { s_registry = IAutomationRegistryConsumer(registry); i_target = target; i_logic = logic; } /** * @notice forward is called by the registry and forwards the call to the target * @param gasAmount is the amount of gas to use in the call * @param data is the 4 bytes function selector + arbitrary function data * @return success indicating whether the target call succeeded or failed */ function forward(uint256 gasAmount, bytes memory data) external returns (bool success, uint256 gasUsed) { if (msg.sender != address(s_registry)) revert(); address target = i_target; gasUsed = gasleft(); assembly { let g := gas() // Compute g -= PERFORM_GAS_CUSHION and check for underflow if lt(g, PERFORM_GAS_CUSHION) { revert(0, 0) } g := sub(g, PERFORM_GAS_CUSHION) // if g - g//64 <= gasAmount, revert // (we subtract g//64 because of EIP-150) if iszero(gt(sub(g, div(g, 64)), gasAmount)) { revert(0, 0) } // solidity calls check that a contract actually exists at the destination, so we do the same if iszero(extcodesize(target)) { revert(0, 0) } // call with exact gas success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) } gasUsed = gasUsed - gasleft(); return (success, gasUsed); } function getTarget() external view returns (address) { return i_target; } fallback() external { // copy to memory for assembly access address logic = i_logic; // copied directly from OZ's Proxy contract assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), logic, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; /** * @title Chainable - the contract size limit nullifier * @notice Chainable is designed to link together a "chain" of contracts through fallback functions * and delegatecalls. All code is executed in the context of the head of the chain, the "master" contract. */ contract Chainable { /** * @dev addresses of the next contract in the chain **have to be immutable/constant** or the system won't work */ address private immutable i_FALLBACK_ADDRESS; /** * @param fallbackAddress the address of the next contract in the chain */ constructor(address fallbackAddress) { i_FALLBACK_ADDRESS = fallbackAddress; } /** * @notice returns the address of the next contract in the chain */ function fallbackTo() external view returns (address) { return i_FALLBACK_ADDRESS; } /** * @notice the fallback function routes the call to the next contract in the chain * @dev most of the implementation is copied directly from OZ's Proxy contract */ // solhint-disable-next-line no-complex-fallback fallback() external payable { // copy to memory for assembly access address next = i_FALLBACK_ADDRESS; // copied directly from OZ's Proxy contract assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the next contract. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), next, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable-next-line interface-starts-with-i interface AutomationCompatibleInterface { /** * @notice method that is simulated by the keepers to see if any work actually * needs to be performed. This method does does not actually need to be * executable, and since it is only ever simulated it can consume lots of gas. * @dev To ensure that it is never called, you may want to add the * cannotExecute modifier from KeeperBase to your implementation of this * method. * @param checkData specified in the upkeep registration so it is always the * same for a registered upkeep. This can easily be broken down into specific * arguments using `abi.decode`, so multiple upkeeps can be registered on the * same contract and easily differentiated by the contract. * @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 checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData); /** * @notice method that is actually executed by the keepers, via the registry. * The data returned by the checkUpkeep simulation will be passed into * this method to actually be executed. * @dev The input to this method should not be trusted, and the caller of the * method should not even be restricted to any single registry. Anyone should * be able call it, and the input should be validated, there is no guarantee * that the data passed in is the performData returned from checkUpkeep. This * could happen due to malicious keepers, racing keepers, or simply a state * change while the performUpkeep transaction is waiting for confirmation. * Always validate the data passed in. * @param performData is the 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 not be trusted, and should be * validated against the contract's current state. */ function performUpkeep(bytes calldata performData) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {IAutomationRegistryConsumer} from "./IAutomationRegistryConsumer.sol"; interface IAutomationForwarder is ITypeAndVersion { function forward(uint256 gasAmount, bytes memory data) external returns (bool success, uint256 gasUsed); function updateRegistry(address newRegistry) external; function getRegistry() external view returns (IAutomationRegistryConsumer); function getTarget() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** * @notice IAutomationRegistryConsumer defines the LTS user-facing interface that we intend to maintain for * across upgrades. As long as users use functions from within this interface, their upkeeps will retain * backwards compatability across migrations. * @dev Functions can be added to this interface, but not removed. */ interface IAutomationRegistryConsumer { function getBalance(uint256 id) external view returns (uint96 balance); function getMinBalance(uint256 id) external view returns (uint96 minBalance); function cancelUpkeep(uint256 id) external; function pauseUpkeep(uint256 id) external; function unpauseUpkeep(uint256 id) external; function addFunds(uint256 id, uint96 amount) external; function withdrawFunds(uint256 id, address to) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; interface IAutomationV21PlusCommon { // registry events event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig); event CancelledUpkeepReport(uint256 indexed id, bytes trigger); event ConfigSet( uint32 previousConfigBlockNumber, bytes32 configDigest, uint64 configCount, address[] signers, address[] transmitters, uint8 f, bytes onchainConfig, uint64 offchainConfigVersion, bytes offchainConfig ); event DedupKeyAdded(bytes32 indexed dedupKey); event InsufficientFundsUpkeepReport(uint256 indexed id, bytes trigger); event OwnershipTransferred(address indexed from, address indexed to); event OwnershipTransferRequested(address indexed from, address indexed to); event Paused(address account); event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); event PayeesUpdated(address[] transmitters, address[] payees); event PaymentWithdrawn(address indexed transmitter, uint256 indexed amount, address indexed to, address payee); event ReorgedUpkeepReport(uint256 indexed id, bytes trigger); event StaleUpkeepReport(uint256 indexed id, bytes trigger); event Transmitted(bytes32 configDigest, uint32 epoch); event Unpaused(address account); // upkeep events event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); event UpkeepPaused(uint256 indexed id); event UpkeepPerformed( uint256 indexed id, bool indexed success, uint96 totalPayment, uint256 gasUsed, uint256 gasOverhead, bytes trigger ); event UpkeepPrivilegeConfigSet(uint256 indexed id, bytes privilegeConfig); event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); event UpkeepRegistered(uint256 indexed id, uint32 performGas, address admin); event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); event UpkeepUnpaused(uint256 indexed id); /** * @notice structure of trigger for log triggers */ struct LogTriggerConfig { address contractAddress; uint8 filterSelector; // denotes which topics apply to filter ex 000, 101, 111...only last 3 bits apply bytes32 topic0; bytes32 topic1; bytes32 topic2; bytes32 topic3; } /// @dev Report transmitted by OCR to transmit function struct Report { uint256 fastGasWei; uint256 linkNative; uint256[] upkeepIds; uint256[] gasLimits; bytes[] triggers; bytes[] performDatas; } /** * @notice all information about an upkeep * @dev only used in return values * @dev this will likely be deprecated in a future version of the registry * @member target the contract which needs to be serviced * @member performGas the gas limit of upkeep execution * @member checkData the checkData bytes for this upkeep * @member balance the balance of this upkeep * @member admin for this upkeep * @member maxValidBlocknumber until which block this upkeep is valid * @member lastPerformedBlockNumber the last block number when this upkeep was performed * @member amountSpent the amount this upkeep has spent * @member paused if this upkeep has been paused * @member offchainConfig the off-chain config of this upkeep */ struct UpkeepInfoLegacy { address target; uint32 performGas; bytes checkData; uint96 balance; address admin; uint64 maxValidBlocknumber; uint32 lastPerformedBlockNumber; uint96 amountSpent; bool paused; bytes offchainConfig; } /** * @notice the trigger structure conditional trigger type */ struct ConditionalTrigger { uint32 blockNum; bytes32 blockHash; } /** * @notice the trigger structure of log upkeeps * @dev NOTE that blockNum / blockHash describe the block used for the callback, * not necessarily the block number that the log was emitted in!!!! */ struct LogTrigger { bytes32 logBlockHash; bytes32 txHash; uint32 logIndex; uint32 blockNum; bytes32 blockHash; } /** * @notice state of the registry * @dev only used in params and return values * @dev this will likely be deprecated in a future version of the registry in favor of individual getters * @member nonce used for ID generation * @member ownerLinkBalance withdrawable balance of LINK by contract owner * @member expectedLinkBalance the expected balance of LINK of the registry * @member totalPremium the total premium collected on registry so far * @member numUpkeeps total number of upkeeps on the registry * @member configCount ordinal number of current config, out of all configs applied to this contract so far * @member latestConfigBlockNumber last block at which this config was set * @member latestConfigDigest domain-separation tag for current config * @member latestEpoch for which a report was transmitted * @member paused freeze on execution scoped to the entire registry */ // solhint-disable-next-line gas-struct-packing struct StateLegacy { uint32 nonce; uint96 ownerLinkBalance; uint256 expectedLinkBalance; uint96 totalPremium; uint256 numUpkeeps; uint32 configCount; uint32 latestConfigBlockNumber; bytes32 latestConfigDigest; uint32 latestEpoch; bool paused; } /** * @notice OnchainConfigLegacy of the registry * @dev only used in params and return values * @member paymentPremiumPPB payment premium rate oracles receive on top of * being reimbursed for gas, measured in parts per billion * @member flatFeeMicroLink flat fee paid to oracles for performing upkeeps, * priced in MicroLink; can be used in conjunction with or independently of * paymentPremiumPPB * @member checkGasLimit gas limit when checking for upkeep * @member stalenessSeconds number of seconds that is allowed for feed data to * be stale before switching to the fallback pricing * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price * when calculating the payment ceiling for keepers * @member minUpkeepSpend minimum LINK that an upkeep must spend before cancelling * @member maxPerformGas max performGas allowed for an upkeep on this registry * @member maxCheckDataSize max length of checkData bytes * @member maxPerformDataSize max length of performData bytes * @member maxRevertDataSize max length of revertData bytes * @member fallbackGasPrice gas price used if the gas price feed is stale * @member fallbackLinkPrice LINK price used if the LINK price feed is stale * @member transcoder address of the transcoder contract * @member registrars addresses of the registrar contracts * @member upkeepPrivilegeManager address which can set privilege for upkeeps */ // solhint-disable-next-line gas-struct-packing struct OnchainConfigLegacy { uint32 paymentPremiumPPB; uint32 flatFeeMicroLink; // min 0.000001 LINK, max 4294 LINK uint32 checkGasLimit; uint24 stalenessSeconds; uint16 gasCeilingMultiplier; uint96 minUpkeepSpend; uint32 maxPerformGas; uint32 maxCheckDataSize; uint32 maxPerformDataSize; uint32 maxRevertDataSize; uint256 fallbackGasPrice; uint256 fallbackLinkPrice; address transcoder; address[] registrars; address upkeepPrivilegeManager; } function checkUpkeep( uint256 id, bytes memory triggerData ) external view returns ( bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed, uint256 gasLimit, uint256 fastGasWei, uint256 linkNative ); function checkUpkeep( uint256 id ) external view returns ( bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed, uint256 gasLimit, uint256 fastGasWei, uint256 linkNative ); function simulatePerformUpkeep( uint256 id, bytes memory performData ) external view returns (bool success, uint256 gasUsed); function executeCallback( uint256 id, bytes memory payload ) external returns (bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed); function checkCallback( uint256 id, bytes[] memory values, bytes memory extraData ) external view returns (bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed); function typeAndVersion() external view returns (string memory); function addFunds(uint256 id, uint96 amount) external; function cancelUpkeep(uint256 id) external; function getUpkeepPrivilegeConfig(uint256 upkeepId) external view returns (bytes memory); function hasDedupKey(bytes32 dedupKey) external view returns (bool); function getUpkeepTriggerConfig(uint256 upkeepId) external view returns (bytes memory); function getUpkeep(uint256 id) external view returns (UpkeepInfoLegacy memory upkeepInfo); function getMinBalance(uint256 id) external view returns (uint96); function getState() external view returns ( StateLegacy memory state, OnchainConfigLegacy memory config, address[] memory signers, address[] memory transmitters, uint8 f ); function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; function setUpkeepPrivilegeConfig(uint256 upkeepId, bytes memory newPrivilegeConfig) external; function pauseUpkeep(uint256 id) external; function unpauseUpkeep(uint256 id) external; function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); function pause() external; function setUpkeepCheckData(uint256 id, bytes memory newCheckData) external; function setUpkeepTriggerConfig(uint256 id, bytes memory triggerConfig) external; function owner() external view returns (address); function getTriggerType(uint256 upkeepId) external pure returns (uint8); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; interface IChainModule { // retrieve the native block number of a chain. e.g. L2 block number on Arbitrum function blockNumber() external view returns (uint256); // retrieve the native block hash of a chain. function blockHash(uint256) external view returns (bytes32); // retrieve the L1 data fee for a L2 transaction. it should return 0 for L1 chains and // L2 chains which don't have L1 fee component. it uses msg.data to estimate L1 data so // it must be used with a transaction. Return value in wei. function getCurrentL1Fee(uint256 dataSize) external view returns (uint256); // retrieve the L1 data fee for a L2 simulation. it should return 0 for L1 chains and // L2 chains which don't have L1 fee component. Return value in wei. function getMaxL1Fee(uint256 dataSize) external view returns (uint256); // Returns an upper bound on execution gas cost for one invocation of blockNumber(), // one invocation of blockHash() and one invocation of getCurrentL1Fee(). // Returns two values, first value indicates a fixed cost and the second value is // the cost per msg.data byte (As some chain module's getCurrentL1Fee execution cost // scales with calldata size) function getGasOverhead() external view returns (uint256 chainModuleFixedOverhead, uint256 chainModulePerByteOverhead); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @member index the index of the log in the block. 0 for the first log * @member timestamp the timestamp of the block containing the log * @member txHash the hash of the transaction containing the log * @member blockNumber the number of the block containing the log * @member blockHash the hash of the block containing the log * @member source the address of the contract that emitted the log * @member topics the indexed topics of the log * @member data the data of the log */ struct Log { uint256 index; uint256 timestamp; bytes32 txHash; uint256 blockNumber; bytes32 blockHash; address source; bytes32[] topics; bytes data; } interface ILogAutomation { /** * @notice method that is simulated by the keepers to see if any work actually * needs to be performed. This method does does not actually need to be * executable, and since it is only ever simulated it can consume lots of gas. * @dev To ensure that it is never called, you may want to add the * cannotExecute modifier from KeeperBase to your implementation of this * method. * @param log the raw log data matching the filter that this contract has * registered as a trigger * @param checkData user-specified extra data to provide context to this upkeep * @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 checkLog( Log calldata log, bytes memory checkData ) external returns (bool upkeepNeeded, bytes memory performData); /** * @notice method that is actually executed by the keepers, via the registry. * The data returned by the checkUpkeep simulation will be passed into * this method to actually be executed. * @dev The input to this method should not be trusted, and the caller of the * method should not even be restricted to any single registry. Anyone should * be able call it, and the input should be validated, there is no guarantee * that the data passed in is the performData returned from checkUpkeep. This * could happen due to malicious keepers, racing keepers, or simply a state * change while the performUpkeep transaction is waiting for confirmation. * Always validate the data passed in. * @param performData is the 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 not be trusted, and should be * validated against the contract's current state. */ function performUpkeep(bytes calldata performData) external; }
// SPDX-License-Identifier: MIT /** * @notice This is a deprecated interface. Please use AutomationCompatibleInterface directly. */ pragma solidity ^0.8.0; // solhint-disable-next-line no-unused-import import {AutomationCompatibleInterface as KeeperCompatibleInterface} from "./AutomationCompatibleInterface.sol";
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable-next-line interface-starts-with-i interface MigratableKeeperRegistryInterfaceV2 { /** * @notice Migrates upkeeps from one registry to another, including LINK and upkeep params. * Only callable by the upkeep admin. All upkeeps must have the same admin. Can only migrate active upkeeps. * @param upkeepIDs ids of upkeeps to migrate * @param destination the address of the registry to migrate to */ function migrateUpkeeps(uint256[] calldata upkeepIDs, address destination) external; /** * @notice Called by other registries when migrating upkeeps. Only callable by other registries. * @param encodedUpkeeps abi encoding of upkeeps to import - decoded by the transcoder */ function receiveUpkeeps(bytes calldata encodedUpkeeps) external; /** * @notice Specifies the version of upkeep data that this registry requires in order to import */ function upkeepVersion() external view returns (uint8 version); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable-next-line interface-starts-with-i interface StreamsLookupCompatibleInterface { error StreamsLookup(string feedParamKey, string[] feeds, string timeParamKey, uint256 time, bytes extraData); /** * @notice any contract which wants to utilize StreamsLookup feature needs to * implement this interface as well as the automation compatible interface. * @param values an array of bytes returned from data streams endpoint. * @param extraData context data from streams lookup process. * @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 checkCallback( bytes[] memory values, bytes memory extraData ) external view returns (bool upkeepNeeded, bytes memory performData); /** * @notice this is a new, optional function in streams lookup. It is meant to surface streams lookup errors. * @param errCode an uint value that represents the streams lookup error code. * @param extraData context data from streams lookup process. * @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 view returns (bool upkeepNeeded, bytes memory performData); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable-next-line interface-starts-with-i interface UpkeepTranscoderInterfaceV2 { function transcodeUpkeeps( uint8 fromVersion, uint8 toVersion, bytes calldata encodedUpkeeps ) external view returns (bytes memory); }
// abi-checksum: 0xacb9d0daec3e3bed110c8cf95bdbe7280a21ce61519d4cd496f802884bf05524 // SPDX-License-Identifier: MIT // !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.6.6. SEE SOURCE BELOW. !! pragma solidity ^0.8.4; interface IAutomationRegistryMaster2_3 { error ArrayHasNoEntries(); error CannotCancel(); error CheckDataExceedsLimit(); error ConfigDigestMismatch(); error DuplicateEntry(); error DuplicateSigners(); error GasLimitCanOnlyIncrease(); error GasLimitOutsideRange(); error IncorrectNumberOfFaultyOracles(); error IncorrectNumberOfSignatures(); error IncorrectNumberOfSigners(); error IndexOutOfRange(); error InsufficientBalance(uint256 available, uint256 requested); error InsufficientLinkLiquidity(); error InvalidDataLength(); error InvalidFeed(); error InvalidPayee(); error InvalidRecipient(); error InvalidReport(); error InvalidSigner(); error InvalidToken(); error InvalidTransmitter(); error InvalidTrigger(); error InvalidTriggerType(); error MigrationNotPermitted(); error MustSettleOffchain(); error MustSettleOnchain(); error NotAContract(); error OnlyActiveSigners(); error OnlyActiveTransmitters(); error OnlyCallableByAdmin(); error OnlyCallableByLINKToken(); error OnlyCallableByOwnerOrAdmin(); error OnlyCallableByOwnerOrRegistrar(); error OnlyCallableByPayee(); error OnlyCallableByProposedAdmin(); error OnlyCallableByProposedPayee(); error OnlyCallableByUpkeepPrivilegeManager(); error OnlyFinanceAdmin(); error OnlyPausedUpkeep(); error OnlySimulatedBackend(); error OnlyUnpausedUpkeep(); error ParameterLengthError(); error ReentrantCall(); error RegistryPaused(); error RepeatedSigner(); error RepeatedTransmitter(); error TargetCheckReverted(bytes reason); error TooManyOracles(); error TranscoderNotSet(); error TransferFailed(); error UpkeepAlreadyExists(); error UpkeepCancelled(); error UpkeepNotCanceled(); error UpkeepNotNeeded(); error ValueNotChanged(); error ZeroAddressNotAllowed(); event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig); event BillingConfigOverridden(uint256 indexed id, AutomationRegistryBase2_3.BillingOverrides overrides); event BillingConfigOverrideRemoved(uint256 indexed id); event BillingConfigSet(address indexed token, AutomationRegistryBase2_3.BillingConfig config); event CancelledUpkeepReport(uint256 indexed id, bytes trigger); event ChainSpecificModuleUpdated(address newModule); event ConfigSet( uint32 previousConfigBlockNumber, bytes32 configDigest, uint64 configCount, address[] signers, address[] transmitters, uint8 f, bytes onchainConfig, uint64 offchainConfigVersion, bytes offchainConfig ); event DedupKeyAdded(bytes32 indexed dedupKey); event FeesWithdrawn(address indexed assetAddress, address indexed recipient, uint256 amount); event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); event InsufficientFundsUpkeepReport(uint256 indexed id, bytes trigger); event NOPsSettledOffchain(address[] payees, uint256[] payments); event OwnershipTransferRequested(address indexed from, address indexed to); event OwnershipTransferred(address indexed from, address indexed to); event Paused(address account); event PayeesUpdated(address[] transmitters, address[] payees); event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); event PaymentWithdrawn(address indexed transmitter, uint256 indexed amount, address indexed to, address payee); event ReorgedUpkeepReport(uint256 indexed id, bytes trigger); event StaleUpkeepReport(uint256 indexed id, bytes trigger); event Transmitted(bytes32 configDigest, uint32 epoch); event Unpaused(address account); event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); event UpkeepCharged(uint256 indexed id, AutomationRegistryBase2_3.PaymentReceipt receipt); event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); event UpkeepPaused(uint256 indexed id); event UpkeepPerformed( uint256 indexed id, bool indexed success, uint96 totalPayment, uint256 gasUsed, uint256 gasOverhead, bytes trigger ); event UpkeepPrivilegeConfigSet(uint256 indexed id, bytes privilegeConfig); event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); event UpkeepRegistered(uint256 indexed id, uint32 performGas, address admin); event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); event UpkeepUnpaused(uint256 indexed id); fallback() external payable; function acceptOwnership() external; function fallbackTo() external view returns (address); function latestConfigDetails() external view returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest); function latestConfigDigestAndEpoch() external view returns (bool scanLogs, bytes32 configDigest, uint32 epoch); function owner() external view returns (address); function setConfig( address[] memory signers, address[] memory transmitters, uint8 f, bytes memory onchainConfigBytes, uint64 offchainConfigVersion, bytes memory offchainConfig ) external; function setConfigTypeSafe( address[] memory signers, address[] memory transmitters, uint8 f, AutomationRegistryBase2_3.OnchainConfig memory onchainConfig, uint64 offchainConfigVersion, bytes memory offchainConfig, address[] memory billingTokens, AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs ) external; function transferOwnership(address to) external; function transmit( bytes32[3] memory reportContext, bytes memory rawReport, bytes32[] memory rs, bytes32[] memory ss, bytes32 rawVs ) external; function typeAndVersion() external view returns (string memory); function cancelUpkeep(uint256 id) external; function migrateUpkeeps(uint256[] memory ids, address destination) external; function onTokenTransfer(address sender, uint256 amount, bytes memory data) external; function receiveUpkeeps(bytes memory encodedUpkeeps) external; function registerUpkeep( address target, uint32 gasLimit, address admin, uint8 triggerType, address billingToken, bytes memory checkData, bytes memory triggerConfig, bytes memory offchainConfig ) external returns (uint256 id); function acceptUpkeepAdmin(uint256 id) external; function addFunds(uint256 id, uint96 amount) external payable; function checkCallback( uint256 id, bytes[] memory values, bytes memory extraData ) external view returns (bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed); function checkUpkeep( uint256 id, bytes memory triggerData ) external view returns ( bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed, uint256 gasLimit, uint256 fastGasWei, uint256 linkUSD ); function checkUpkeep( uint256 id ) external view returns ( bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed, uint256 gasLimit, uint256 fastGasWei, uint256 linkUSD ); function executeCallback( uint256 id, bytes memory payload ) external returns (bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed); function pauseUpkeep(uint256 id) external; function removeBillingOverrides(uint256 id) external; function setBillingOverrides(uint256 id, AutomationRegistryBase2_3.BillingOverrides memory billingOverrides) external; function setUpkeepCheckData(uint256 id, bytes memory newCheckData) external; function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; function setUpkeepOffchainConfig(uint256 id, bytes memory config) external; function setUpkeepTriggerConfig(uint256 id, bytes memory triggerConfig) external; function simulatePerformUpkeep( uint256 id, bytes memory performData ) external view returns (bool success, uint256 gasUsed); function transferUpkeepAdmin(uint256 id, address proposed) external; function unpauseUpkeep(uint256 id) external; function withdrawERC20Fees(address asset, address to, uint256 amount) external; function withdrawFunds(uint256 id, address to) external; function withdrawLink(address to, uint256 amount) external; function acceptPayeeship(address transmitter) external; function disableOffchainPayments() external; function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); function getAdminPrivilegeConfig(address admin) external view returns (bytes memory); function getAllowedReadOnlyAddress() external view returns (address); function getAutomationForwarderLogic() external view returns (address); function getAvailableERC20ForPayment(address billingToken) external view returns (uint256); function getBalance(uint256 id) external view returns (uint96 balance); function getBillingConfig( address billingToken ) external view returns (AutomationRegistryBase2_3.BillingConfig memory); function getBillingOverrides( uint256 upkeepID ) external view returns (AutomationRegistryBase2_3.BillingOverrides memory); function getBillingOverridesEnabled(uint256 upkeepID) external view returns (bool); function getBillingToken(uint256 upkeepID) external view returns (address); function getBillingTokenConfig(address token) external view returns (AutomationRegistryBase2_3.BillingConfig memory); function getBillingTokens() external view returns (address[] memory); function getCancellationDelay() external pure returns (uint256); function getChainModule() external view returns (address chainModule); function getConditionalGasOverhead() external pure returns (uint256); function getConfig() external view returns (AutomationRegistryBase2_3.OnchainConfig memory); function getFallbackNativePrice() external view returns (uint256); function getFastGasFeedAddress() external view returns (address); function getForwarder(uint256 upkeepID) external view returns (address); function getHotVars() external view returns (AutomationRegistryBase2_3.HotVars memory); function getLinkAddress() external view returns (address); function getLinkUSDFeedAddress() external view returns (address); function getLogGasOverhead() external pure returns (uint256); function getMaxPaymentForGas( uint256 id, uint8 triggerType, uint32 gasLimit, address billingToken ) external view returns (uint96 maxPayment); function getMinBalance(uint256 id) external view returns (uint96); function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance); function getNativeUSDFeedAddress() external view returns (address); function getNumUpkeeps() external view returns (uint256); function getPayoutMode() external view returns (uint8); function getPeerRegistryMigrationPermission(address peer) external view returns (uint8); function getPerPerformByteGasOverhead() external pure returns (uint256); function getPerSignerGasOverhead() external pure returns (uint256); function getReorgProtectionEnabled() external view returns (bool reorgProtectionEnabled); function getReserveAmount(address billingToken) external view returns (uint256); function getSignerInfo(address query) external view returns (bool active, uint8 index); function getState() external view returns ( IAutomationV21PlusCommon.StateLegacy memory state, IAutomationV21PlusCommon.OnchainConfigLegacy memory config, address[] memory signers, address[] memory transmitters, uint8 f ); function getStorage() external view returns (AutomationRegistryBase2_3.Storage memory); function getTransmitCalldataFixedBytesOverhead() external pure returns (uint256); function getTransmitCalldataPerSignerBytesOverhead() external pure returns (uint256); function getTransmitterInfo( address query ) external view returns (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee); function getTransmittersWithPayees() external view returns (AutomationRegistryBase2_3.TransmitterPayeeInfo[] memory); function getTriggerType(uint256 upkeepId) external pure returns (uint8); function getUpkeep(uint256 id) external view returns (IAutomationV21PlusCommon.UpkeepInfoLegacy memory upkeepInfo); function getUpkeepPrivilegeConfig(uint256 upkeepId) external view returns (bytes memory); function getUpkeepTriggerConfig(uint256 upkeepId) external view returns (bytes memory); function getWrappedNativeTokenAddress() external view returns (address); function hasDedupKey(bytes32 dedupKey) external view returns (bool); function linkAvailableForPayment() external view returns (int256); function pause() external; function setAdminPrivilegeConfig(address admin, bytes memory newPrivilegeConfig) external; function setPayees(address[] memory payees) external; function setPeerRegistryMigrationPermission(address peer, uint8 permission) external; function setUpkeepPrivilegeConfig(uint256 upkeepId, bytes memory newPrivilegeConfig) external; function settleNOPsOffchain() external; function supportsBillingToken(address token) external view returns (bool); function transferPayeeship(address transmitter, address proposed) external; function unpause() external; function upkeepVersion() external pure returns (uint8); function withdrawPayment(address from, address to) external; } interface AutomationRegistryBase2_3 { struct BillingOverrides { uint32 gasFeePPB; uint24 flatFeeMilliCents; } struct BillingConfig { uint32 gasFeePPB; uint24 flatFeeMilliCents; address priceFeed; uint8 decimals; uint256 fallbackPrice; uint96 minSpend; } struct PaymentReceipt { uint96 gasChargeInBillingToken; uint96 premiumInBillingToken; uint96 gasReimbursementInJuels; uint96 premiumInJuels; address billingToken; uint96 linkUSD; uint96 nativeUSD; uint96 billingUSD; } struct OnchainConfig { uint32 checkGasLimit; uint32 maxPerformGas; uint32 maxCheckDataSize; address transcoder; bool reorgProtectionEnabled; uint24 stalenessSeconds; uint32 maxPerformDataSize; uint32 maxRevertDataSize; address upkeepPrivilegeManager; uint16 gasCeilingMultiplier; address financeAdmin; uint256 fallbackGasPrice; uint256 fallbackLinkPrice; uint256 fallbackNativePrice; address[] registrars; address chainModule; } struct HotVars { uint96 totalPremium; uint32 latestEpoch; uint24 stalenessSeconds; uint16 gasCeilingMultiplier; uint8 f; bool paused; bool reentrancyGuard; bool reorgProtectionEnabled; address chainModule; } struct Storage { address transcoder; uint32 checkGasLimit; uint32 maxPerformGas; uint32 nonce; address upkeepPrivilegeManager; uint32 configCount; uint32 latestConfigBlockNumber; uint32 maxCheckDataSize; address financeAdmin; uint32 maxPerformDataSize; uint32 maxRevertDataSize; } struct TransmitterPayeeInfo { address transmitterAddress; address payeeAddress; } } interface IAutomationV21PlusCommon { struct StateLegacy { uint32 nonce; uint96 ownerLinkBalance; uint256 expectedLinkBalance; uint96 totalPremium; uint256 numUpkeeps; uint32 configCount; uint32 latestConfigBlockNumber; bytes32 latestConfigDigest; uint32 latestEpoch; bool paused; } struct OnchainConfigLegacy { uint32 paymentPremiumPPB; uint32 flatFeeMicroLink; uint32 checkGasLimit; uint24 stalenessSeconds; uint16 gasCeilingMultiplier; uint96 minUpkeepSpend; uint32 maxPerformGas; uint32 maxCheckDataSize; uint32 maxPerformDataSize; uint32 maxRevertDataSize; uint256 fallbackGasPrice; uint256 fallbackLinkPrice; address transcoder; address[] registrars; address upkeepPrivilegeManager; } struct UpkeepInfoLegacy { address target; uint32 performGas; bytes checkData; uint96 balance; address admin; uint64 maxValidBlocknumber; uint32 lastPerformedBlockNumber; uint96 amountSpent; bool paused; bytes offchainConfig; } } // THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON: /* [{"inputs":[{"internalType":"contract AutomationRegistryLogicA2_3","name":"logicA","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayHasNoEntries","type":"error"},{"inputs":[],"name":"CannotCancel","type":"error"},{"inputs":[],"name":"CheckDataExceedsLimit","type":"error"},{"inputs":[],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"DuplicateSigners","type":"error"},{"inputs":[],"name":"GasLimitCanOnlyIncrease","type":"error"},{"inputs":[],"name":"GasLimitOutsideRange","type":"error"},{"inputs":[],"name":"IncorrectNumberOfFaultyOracles","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSignatures","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSigners","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientLinkLiquidity","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidFeed","type":"error"},{"inputs":[],"name":"InvalidPayee","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReport","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidTransmitter","type":"error"},{"inputs":[],"name":"InvalidTrigger","type":"error"},{"inputs":[],"name":"InvalidTriggerType","type":"error"},{"inputs":[],"name":"MigrationNotPermitted","type":"error"},{"inputs":[],"name":"MustSettleOffchain","type":"error"},{"inputs":[],"name":"MustSettleOnchain","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"OnlyActiveSigners","type":"error"},{"inputs":[],"name":"OnlyActiveTransmitters","type":"error"},{"inputs":[],"name":"OnlyCallableByAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByLINKToken","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrRegistrar","type":"error"},{"inputs":[],"name":"OnlyCallableByPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByUpkeepPrivilegeManager","type":"error"},{"inputs":[],"name":"OnlyFinanceAdmin","type":"error"},{"inputs":[],"name":"OnlyPausedUpkeep","type":"error"},{"inputs":[],"name":"OnlySimulatedBackend","type":"error"},{"inputs":[],"name":"OnlyUnpausedUpkeep","type":"error"},{"inputs":[],"name":"ParameterLengthError","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"RegistryPaused","type":"error"},{"inputs":[],"name":"RepeatedSigner","type":"error"},{"inputs":[],"name":"RepeatedTransmitter","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TargetCheckReverted","type":"error"},{"inputs":[],"name":"TooManyOracles","type":"error"},{"inputs":[],"name":"TranscoderNotSet","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UpkeepAlreadyExists","type":"error"},{"inputs":[],"name":"UpkeepCancelled","type":"error"},{"inputs":[],"name":"UpkeepNotCanceled","type":"error"},{"inputs":[],"name":"UpkeepNotNeeded","type":"error"},{"inputs":[],"name":"ValueNotChanged","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"AdminPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"}],"indexed":false,"internalType":"struct AutomationRegistryBase2_3.BillingOverrides","name":"overrides","type":"tuple"}],"name":"BillingConfigOverridden","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"BillingConfigOverrideRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"},{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"fallbackPrice","type":"uint256"},{"internalType":"uint96","name":"minSpend","type":"uint96"}],"indexed":false,"internalType":"struct AutomationRegistryBase2_3.BillingConfig","name":"config","type":"tuple"}],"name":"BillingConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"CancelledUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newModule","type":"address"}],"name":"ChainSpecificModuleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"previousConfigBlockNumber","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"configCount","type":"uint64"},{"indexed":false,"internalType":"address[]","name":"signers","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"uint8","name":"f","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"onchainConfig","type":"bytes"},{"indexed":false,"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"DedupKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"FundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"InsufficientFundsUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"payments","type":"uint256[]"}],"name":"NOPsSettledOffchain","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"}],"name":"PayeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"PaymentWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"ReorgedUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"StaleUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"}],"name":"Transmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"atBlockHeight","type":"uint64"}],"name":"UpkeepCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"uint96","name":"gasChargeInBillingToken","type":"uint96"},{"internalType":"uint96","name":"premiumInBillingToken","type":"uint96"},{"internalType":"uint96","name":"gasReimbursementInJuels","type":"uint96"},{"internalType":"uint96","name":"premiumInJuels","type":"uint96"},{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"},{"internalType":"uint96","name":"linkUSD","type":"uint96"},{"internalType":"uint96","name":"nativeUSD","type":"uint96"},{"internalType":"uint96","name":"billingUSD","type":"uint96"}],"indexed":false,"internalType":"struct AutomationRegistryBase2_3.PaymentReceipt","name":"receipt","type":"tuple"}],"name":"UpkeepCharged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"UpkeepCheckDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"gasLimit","type":"uint96"}],"name":"UpkeepGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"UpkeepMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"UpkeepOffchainConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint96","name":"totalPayment","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"gasUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"UpkeepPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"UpkeepPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"importedFrom","type":"address"}],"name":"UpkeepReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"performGas","type":"uint32"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"UpkeepTriggerConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepUnpaused","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fallbackTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestConfigDetails","outputs":[{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"bytes32","name":"configDigest","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestConfigDigestAndEpoch","outputs":[{"internalType":"bool","name":"scanLogs","type":"bool"},{"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"internalType":"uint32","name":"epoch","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfigBytes","type":"bytes"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"components":[{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"address","name":"financeAdmin","type":"address"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackNativePrice","type":"uint256"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"contract IChainModule","name":"chainModule","type":"address"}],"internalType":"struct AutomationRegistryBase2_3.OnchainConfig","name":"onchainConfig","type":"tuple"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"},{"internalType":"contract IERC20Metadata[]","name":"billingTokens","type":"address[]"},{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"},{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"fallbackPrice","type":"uint256"},{"internalType":"uint96","name":"minSpend","type":"uint96"}],"internalType":"struct AutomationRegistryBase2_3.BillingConfig[]","name":"billingConfigs","type":"tuple[]"}],"name":"setConfigTypeSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"rawReport","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract AutomationRegistryLogicB2_3","name":"logicB","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"destination","type":"address"}],"name":"migrateUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedUpkeeps","type":"bytes"}],"name":"receiveUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"enum AutomationRegistryBase2_3.Trigger","name":"triggerType","type":"uint8"},{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract AutomationRegistryLogicC2_3","name":"logicC","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"acceptUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"addFunds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"checkCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_3.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_3.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkUSD","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_3.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkUSD","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"executeCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum AutomationRegistryBase2_3.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"pauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"removeBillingOverrides","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"}],"internalType":"struct AutomationRegistryBase2_3.BillingOverrides","name":"billingOverrides","type":"tuple"}],"name":"setBillingOverrides","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"setUpkeepCheckData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"config","type":"bytes"}],"name":"setUpkeepOffchainConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"setUpkeepTriggerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"simulatePerformUpkeep","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"unpauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"asset","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawERC20Fees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawLink","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"link","type":"address"},{"internalType":"address","name":"linkUSDFeed","type":"address"},{"internalType":"address","name":"nativeUSDFeed","type":"address"},{"internalType":"address","name":"fastGasFeed","type":"address"},{"internalType":"address","name":"automationForwarderLogic","type":"address"},{"internalType":"address","name":"allowedReadOnlyAddress","type":"address"},{"internalType":"enum AutomationRegistryBase2_3.PayoutMode","name":"payoutMode","type":"uint8"},{"internalType":"address","name":"wrappedNativeTokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableOffchainPayments","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"maxCount","type":"uint256"}],"name":"getActiveUpkeepIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"getAdminPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllowedReadOnlyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAutomationForwarderLogic","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getAvailableERC20ForPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getBalance","outputs":[{"internalType":"uint96","name":"balance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getBillingConfig","outputs":[{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"},{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"fallbackPrice","type":"uint256"},{"internalType":"uint96","name":"minSpend","type":"uint96"}],"internalType":"struct AutomationRegistryBase2_3.BillingConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getBillingOverrides","outputs":[{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"}],"internalType":"struct AutomationRegistryBase2_3.BillingOverrides","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getBillingOverridesEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getBillingToken","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"}],"name":"getBillingTokenConfig","outputs":[{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"},{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"fallbackPrice","type":"uint256"},{"internalType":"uint96","name":"minSpend","type":"uint96"}],"internalType":"struct AutomationRegistryBase2_3.BillingConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBillingTokens","outputs":[{"internalType":"contract IERC20Metadata[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancellationDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getChainModule","outputs":[{"internalType":"contract IChainModule","name":"chainModule","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConditionalGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getConfig","outputs":[{"components":[{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"address","name":"financeAdmin","type":"address"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackNativePrice","type":"uint256"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"contract IChainModule","name":"chainModule","type":"address"}],"internalType":"struct AutomationRegistryBase2_3.OnchainConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFallbackNativePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFastGasFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getForwarder","outputs":[{"internalType":"contract IAutomationForwarder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHotVars","outputs":[{"components":[{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bool","name":"reentrancyGuard","type":"bool"},{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"},{"internalType":"contract IChainModule","name":"chainModule","type":"address"}],"internalType":"struct AutomationRegistryBase2_3.HotVars","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkUSDFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLogGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"enum AutomationRegistryBase2_3.Trigger","name":"triggerType","type":"uint8"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getMaxPaymentForGas","outputs":[{"internalType":"uint96","name":"maxPayment","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalance","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalanceForUpkeep","outputs":[{"internalType":"uint96","name":"minBalance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNativeUSDFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumUpkeeps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPayoutMode","outputs":[{"internalType":"enum AutomationRegistryBase2_3.PayoutMode","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"}],"name":"getPeerRegistryMigrationPermission","outputs":[{"internalType":"enum AutomationRegistryBase2_3.MigrationPermission","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPerPerformByteGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getPerSignerGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getReorgProtectionEnabled","outputs":[{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getReserveAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getSignerInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"components":[{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint96","name":"ownerLinkBalance","type":"uint96"},{"internalType":"uint256","name":"expectedLinkBalance","type":"uint256"},{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint256","name":"numUpkeeps","type":"uint256"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"bytes32","name":"latestConfigDigest","type":"bytes32"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct IAutomationV21PlusCommon.StateLegacy","name":"state","type":"tuple"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct IAutomationV21PlusCommon.OnchainConfigLegacy","name":"config","type":"tuple"},{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStorage","outputs":[{"components":[{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"address","name":"financeAdmin","type":"address"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"}],"internalType":"struct AutomationRegistryBase2_3.Storage","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTransmitCalldataFixedBytesOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getTransmitCalldataPerSignerBytesOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getTransmitterInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint96","name":"lastCollected","type":"uint96"},{"internalType":"address","name":"payee","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTransmittersWithPayees","outputs":[{"components":[{"internalType":"address","name":"transmitterAddress","type":"address"},{"internalType":"address","name":"payeeAddress","type":"address"}],"internalType":"struct AutomationRegistryBase2_3.TransmitterPayeeInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getTriggerType","outputs":[{"internalType":"enum AutomationRegistryBase2_3.Trigger","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUpkeep","outputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"performGas","type":"uint32"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"uint64","name":"maxValidBlocknumber","type":"uint64"},{"internalType":"uint32","name":"lastPerformedBlockNumber","type":"uint32"},{"internalType":"uint96","name":"amountSpent","type":"uint96"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct IAutomationV21PlusCommon.UpkeepInfoLegacy","name":"upkeepInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepTriggerConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWrappedNativeTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"hasDedupKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"linkAvailableForPayment","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setAdminPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"},{"internalType":"enum AutomationRegistryBase2_3.MigrationPermission","name":"permission","type":"uint8"}],"name":"setPeerRegistryMigrationPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setUpkeepPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"settleNOPsOffchain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"}],"name":"supportsBillingToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}] */
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IERC20Metadata as IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IWrappedNative is IERC20 { function deposit() external payable; function withdraw(uint256 wad) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; import {AutomationRegistryBase2_3} from "./AutomationRegistryBase2_3.sol"; import {AutomationRegistryLogicA2_3} from "./AutomationRegistryLogicA2_3.sol"; import {AutomationRegistryLogicC2_3} from "./AutomationRegistryLogicC2_3.sol"; import {Chainable} from "../Chainable.sol"; import {OCR2Abstract} from "../../shared/ocr2/OCR2Abstract.sol"; import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; /** * @notice Registry for adding work for Chainlink nodes to perform on client * contracts. Clients must support the AutomationCompatibleInterface interface. */ contract AutomationRegistry2_3 is AutomationRegistryBase2_3, OCR2Abstract, Chainable { using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; /** * @notice versions: * AutomationRegistry 2.3.0: supports native and ERC20 billing * changes flat fee to USD-denominated * adds support for custom billing overrides * AutomationRegistry 2.2.0: moves chain-specific integration code into a separate module * KeeperRegistry 2.1.0: introduces support for log triggers * removes the need for "wrapped perform data" * KeeperRegistry 2.0.2: pass revert bytes as performData when target contract reverts * fixes issue with arbitrum block number * does an early return in case of stale report instead of revert * KeeperRegistry 2.0.1: implements workaround for buggy migrate function in 1.X * KeeperRegistry 2.0.0: implement OCR interface * KeeperRegistry 1.3.0: split contract into Proxy and Logic * account for Arbitrum and Optimism L1 gas fee * allow users to configure upkeeps * KeeperRegistry 1.2.0: allow funding within performUpkeep * allow configurable registry maxPerformGas * add function to let admin change upkeep gas limit * add minUpkeepSpend requirement * upgrade to solidity v0.8 * KeeperRegistry 1.1.0: added flatFeeMicroLink * KeeperRegistry 1.0.0: initial release */ string public constant override typeAndVersion = "AutomationRegistry 2.3.0"; /** * @param logicA the address of the first logic contract * @dev we cast the contract to logicC in order to call logicC functions (via fallback) */ constructor( AutomationRegistryLogicA2_3 logicA ) AutomationRegistryBase2_3( AutomationRegistryLogicC2_3(address(logicA)).getLinkAddress(), AutomationRegistryLogicC2_3(address(logicA)).getLinkUSDFeedAddress(), AutomationRegistryLogicC2_3(address(logicA)).getNativeUSDFeedAddress(), AutomationRegistryLogicC2_3(address(logicA)).getFastGasFeedAddress(), AutomationRegistryLogicC2_3(address(logicA)).getAutomationForwarderLogic(), AutomationRegistryLogicC2_3(address(logicA)).getAllowedReadOnlyAddress(), AutomationRegistryLogicC2_3(address(logicA)).getPayoutMode(), AutomationRegistryLogicC2_3(address(logicA)).getWrappedNativeTokenAddress() ) Chainable(address(logicA)) {} /** * @notice holds the variables used in the transmit function, necessary to avoid stack too deep errors */ struct TransmitVars { uint16 numUpkeepsPassedChecks; uint96 totalReimbursement; uint96 totalPremium; uint256 totalCalldataWeight; } // ================================================================ // | HOT PATH ACTIONS | // ================================================================ /** * @inheritdoc OCR2Abstract */ function transmit( bytes32[3] calldata reportContext, bytes calldata rawReport, bytes32[] calldata rs, bytes32[] calldata ss, bytes32 rawVs ) external override { uint256 gasOverhead = gasleft(); // use this msg.data length check to ensure no extra data is included in the call // 4 is first 4 bytes of the keccak-256 hash of the function signature. ss.length == rs.length so use one of them // 4 + (32 * 3) + (rawReport.length + 32 + 32) + (32 * rs.length + 32 + 32) + (32 * ss.length + 32 + 32) + 32 uint256 requiredLength = 324 + rawReport.length + 64 * rs.length; if (msg.data.length != requiredLength) revert InvalidDataLength(); HotVars memory hotVars = s_hotVars; if (hotVars.paused) revert RegistryPaused(); if (!s_transmitters[msg.sender].active) revert OnlyActiveTransmitters(); // Verify signatures if (s_latestConfigDigest != reportContext[0]) revert ConfigDigestMismatch(); if (rs.length != hotVars.f + 1 || rs.length != ss.length) revert IncorrectNumberOfSignatures(); _verifyReportSignature(reportContext, rawReport, rs, ss, rawVs); Report memory report = _decodeReport(rawReport); uint40 epochAndRound = uint40(uint256(reportContext[1])); uint32 epoch = uint32(epochAndRound >> 8); _handleReport(hotVars, report, gasOverhead); if (epoch > hotVars.latestEpoch) { s_hotVars.latestEpoch = epoch; } } /** * @notice handles the report by performing the upkeeps and updating the state * @param hotVars the hot variables of the registry * @param report the report to be handled (already verified and decoded) * @param gasOverhead the running tally of gas overhead to be split across the upkeeps * @dev had to split this function from transmit() to avoid stack too deep errors * @dev all other internal / private functions are generally defined in the Base contract * we leave this here because it is essentially a continuation of the transmit() function, */ function _handleReport(HotVars memory hotVars, Report memory report, uint256 gasOverhead) private { UpkeepTransmitInfo[] memory upkeepTransmitInfo = new UpkeepTransmitInfo[](report.upkeepIds.length); TransmitVars memory transmitVars = TransmitVars({ numUpkeepsPassedChecks: 0, totalCalldataWeight: 0, totalReimbursement: 0, totalPremium: 0 }); uint256 blocknumber = hotVars.chainModule.blockNumber(); uint256 l1Fee = hotVars.chainModule.getCurrentL1Fee(msg.data.length); for (uint256 i = 0; i < report.upkeepIds.length; i++) { upkeepTransmitInfo[i].upkeep = s_upkeep[report.upkeepIds[i]]; upkeepTransmitInfo[i].triggerType = _getTriggerType(report.upkeepIds[i]); (upkeepTransmitInfo[i].earlyChecksPassed, upkeepTransmitInfo[i].dedupID) = _prePerformChecks( report.upkeepIds[i], blocknumber, report.triggers[i], upkeepTransmitInfo[i], hotVars ); if (upkeepTransmitInfo[i].earlyChecksPassed) { transmitVars.numUpkeepsPassedChecks += 1; } else { continue; } // Actually perform the target upkeep (upkeepTransmitInfo[i].performSuccess, upkeepTransmitInfo[i].gasUsed) = _performUpkeep( upkeepTransmitInfo[i].upkeep.forwarder, report.gasLimits[i], report.performDatas[i] ); // To split L1 fee across the upkeeps, assign a weight to this upkeep based on the length // of the perform data and calldata overhead upkeepTransmitInfo[i].calldataWeight = report.performDatas[i].length + TRANSMIT_CALLDATA_FIXED_BYTES_OVERHEAD + (TRANSMIT_CALLDATA_PER_SIGNER_BYTES_OVERHEAD * (hotVars.f + 1)); transmitVars.totalCalldataWeight += upkeepTransmitInfo[i].calldataWeight; // Deduct the gasUsed by upkeep from the overhead tally - upkeeps pay for their own gas individually gasOverhead -= upkeepTransmitInfo[i].gasUsed; // Store last perform block number / deduping key for upkeep _updateTriggerMarker(report.upkeepIds[i], blocknumber, upkeepTransmitInfo[i]); } // No upkeeps to be performed in this report if (transmitVars.numUpkeepsPassedChecks == 0) { return; } // This is the overall gas overhead that will be split across performed upkeeps // Take upper bound of 16 gas per callData bytes gasOverhead = (gasOverhead - gasleft()) + (16 * msg.data.length) + ACCOUNTING_FIXED_GAS_OVERHEAD; gasOverhead = gasOverhead / transmitVars.numUpkeepsPassedChecks + ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD; { BillingTokenPaymentParams memory billingTokenParams; uint256 nativeUSD = _getNativeUSD(hotVars); for (uint256 i = 0; i < report.upkeepIds.length; i++) { if (upkeepTransmitInfo[i].earlyChecksPassed) { if (i == 0 || upkeepTransmitInfo[i].upkeep.billingToken != upkeepTransmitInfo[i - 1].upkeep.billingToken) { billingTokenParams = _getBillingTokenPaymentParams(hotVars, upkeepTransmitInfo[i].upkeep.billingToken); } PaymentReceipt memory receipt = _handlePayment( hotVars, PaymentParams({ gasLimit: upkeepTransmitInfo[i].gasUsed, gasOverhead: gasOverhead, l1CostWei: (l1Fee * upkeepTransmitInfo[i].calldataWeight) / transmitVars.totalCalldataWeight, fastGasWei: report.fastGasWei, linkUSD: report.linkUSD, nativeUSD: nativeUSD, billingToken: upkeepTransmitInfo[i].upkeep.billingToken, billingTokenParams: billingTokenParams, isTransaction: true }), report.upkeepIds[i], upkeepTransmitInfo[i].upkeep ); transmitVars.totalPremium += receipt.premiumInJuels; transmitVars.totalReimbursement += receipt.gasReimbursementInJuels; emit UpkeepPerformed( report.upkeepIds[i], upkeepTransmitInfo[i].performSuccess, receipt.gasChargeInBillingToken + receipt.premiumInBillingToken, upkeepTransmitInfo[i].gasUsed, gasOverhead, report.triggers[i] ); } } } // record payments to NOPs, all in LINK s_transmitters[msg.sender].balance += transmitVars.totalReimbursement; s_hotVars.totalPremium += transmitVars.totalPremium; s_reserveAmounts[IERC20(address(i_link))] += transmitVars.totalReimbursement + transmitVars.totalPremium; } // ================================================================ // | OCR2ABSTRACT | // ================================================================ /** * @inheritdoc OCR2Abstract * @dev prefer the type-safe version of setConfig (below) whenever possible. The OnchainConfig could differ between registry versions * @dev this function takes up precious space on the root contract, but must be implemented to conform to the OCR2Abstract interface */ function setConfig( address[] memory signers, address[] memory transmitters, uint8 f, bytes memory onchainConfigBytes, uint64 offchainConfigVersion, bytes memory offchainConfig ) external override { (OnchainConfig memory config, IERC20[] memory billingTokens, BillingConfig[] memory billingConfigs) = abi.decode( onchainConfigBytes, (OnchainConfig, IERC20[], BillingConfig[]) ); setConfigTypeSafe( signers, transmitters, f, config, offchainConfigVersion, offchainConfig, billingTokens, billingConfigs ); } /** * @notice sets the configuration for the registry * @param signers the list of permitted signers * @param transmitters the list of permitted transmitters * @param f the maximum tolerance for faulty nodes * @param onchainConfig configuration values that are used on-chain * @param offchainConfigVersion the version of the offchainConfig * @param offchainConfig configuration values that are used off-chain * @param billingTokens the list of valid billing tokens * @param billingConfigs the configurations for each billing token */ function setConfigTypeSafe( address[] memory signers, address[] memory transmitters, uint8 f, OnchainConfig memory onchainConfig, uint64 offchainConfigVersion, bytes memory offchainConfig, IERC20[] memory billingTokens, BillingConfig[] memory billingConfigs ) public onlyOwner { if (signers.length > MAX_NUM_ORACLES) revert TooManyOracles(); if (f == 0) revert IncorrectNumberOfFaultyOracles(); if (signers.length != transmitters.length || signers.length <= 3 * f) revert IncorrectNumberOfSigners(); if (billingTokens.length != billingConfigs.length) revert ParameterLengthError(); // set billing config for tokens _setBillingConfig(billingTokens, billingConfigs); _updateTransmitters(signers, transmitters); s_hotVars = HotVars({ f: f, stalenessSeconds: onchainConfig.stalenessSeconds, gasCeilingMultiplier: onchainConfig.gasCeilingMultiplier, paused: s_hotVars.paused, reentrancyGuard: s_hotVars.reentrancyGuard, totalPremium: s_hotVars.totalPremium, latestEpoch: 0, // DON restarts epoch reorgProtectionEnabled: onchainConfig.reorgProtectionEnabled, chainModule: onchainConfig.chainModule }); uint32 previousConfigBlockNumber = s_storage.latestConfigBlockNumber; uint32 newLatestConfigBlockNumber = uint32(onchainConfig.chainModule.blockNumber()); uint32 newConfigCount = s_storage.configCount + 1; s_storage = Storage({ checkGasLimit: onchainConfig.checkGasLimit, maxPerformGas: onchainConfig.maxPerformGas, transcoder: onchainConfig.transcoder, maxCheckDataSize: onchainConfig.maxCheckDataSize, maxPerformDataSize: onchainConfig.maxPerformDataSize, maxRevertDataSize: onchainConfig.maxRevertDataSize, upkeepPrivilegeManager: onchainConfig.upkeepPrivilegeManager, financeAdmin: onchainConfig.financeAdmin, nonce: s_storage.nonce, configCount: newConfigCount, latestConfigBlockNumber: newLatestConfigBlockNumber }); s_fallbackGasPrice = onchainConfig.fallbackGasPrice; s_fallbackLinkPrice = onchainConfig.fallbackLinkPrice; s_fallbackNativePrice = onchainConfig.fallbackNativePrice; bytes memory onchainConfigBytes = abi.encode(onchainConfig); s_latestConfigDigest = _configDigestFromConfigData( block.chainid, address(this), s_storage.configCount, signers, transmitters, f, onchainConfigBytes, offchainConfigVersion, offchainConfig ); for (uint256 idx = s_registrars.length(); idx > 0; idx--) { s_registrars.remove(s_registrars.at(idx - 1)); } for (uint256 idx = 0; idx < onchainConfig.registrars.length; idx++) { s_registrars.add(onchainConfig.registrars[idx]); } emit ConfigSet( previousConfigBlockNumber, s_latestConfigDigest, s_storage.configCount, signers, transmitters, f, onchainConfigBytes, offchainConfigVersion, offchainConfig ); } /** * @inheritdoc OCR2Abstract * @dev this function takes up precious space on the root contract, but must be implemented to conform to the OCR2Abstract interface */ function latestConfigDetails() external view override returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) { return (s_storage.configCount, s_storage.latestConfigBlockNumber, s_latestConfigDigest); } /** * @inheritdoc OCR2Abstract * @dev this function takes up precious space on the root contract, but must be implemented to conform to the OCR2Abstract interface */ function latestConfigDigestAndEpoch() external view override returns (bool scanLogs, bytes32 configDigest, uint32 epoch) { return (false, s_latestConfigDigest, s_hotVars.latestEpoch); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; import {StreamsLookupCompatibleInterface} from "../interfaces/StreamsLookupCompatibleInterface.sol"; import {ILogAutomation, Log} from "../interfaces/ILogAutomation.sol"; import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; import {KeeperCompatibleInterface} from "../interfaces/KeeperCompatibleInterface.sol"; import {IChainModule} from "../interfaces/IChainModule.sol"; import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; import {IWrappedNative} from "../interfaces/v2_3/IWrappedNative.sol"; /** * @notice Base Keeper Registry contract, contains shared logic between * AutomationRegistry and AutomationRegistryLogic * @dev all errors, events, and internal functions should live here */ // solhint-disable-next-line max-states-count abstract contract AutomationRegistryBase2_3 is ConfirmedOwner { using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; address internal constant ZERO_ADDRESS = address(0); address internal constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; bytes4 internal constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; bytes4 internal constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; bytes4 internal constant CHECK_CALLBACK_SELECTOR = StreamsLookupCompatibleInterface.checkCallback.selector; bytes4 internal constant CHECK_LOG_SELECTOR = ILogAutomation.checkLog.selector; uint256 internal constant PERFORM_GAS_MIN = 2_300; uint256 internal constant CANCELLATION_DELAY = 50; uint256 internal constant PERFORM_GAS_CUSHION = 5_000; uint256 internal constant PPB_BASE = 1_000_000_000; uint32 internal constant UINT32_MAX = type(uint32).max; // The first byte of the mask can be 0, because we only ever have 31 oracles uint256 internal constant ORACLE_MASK = 0x0001010101010101010101010101010101010101010101010101010101010101; uint8 internal constant UPKEEP_VERSION_BASE = 4; // Next block of constants are only used in maxPayment estimation during checkUpkeep simulation // These values are calibrated using hardhat tests which simulate various cases and verify that // the variables result in accurate estimation uint256 internal constant REGISTRY_CONDITIONAL_OVERHEAD = 98_200; // Fixed gas overhead for conditional upkeeps uint256 internal constant REGISTRY_LOG_OVERHEAD = 123_500; // Fixed gas overhead for log upkeeps uint256 internal constant REGISTRY_PER_SIGNER_GAS_OVERHEAD = 5_600; // Value scales with f uint256 internal constant REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD = 24; // Per perform data byte overhead // The overhead (in bytes) in addition to perform data for upkeep sent in calldata // This includes overhead for all struct encoding as well as report signatures // There is a fixed component and a per signer component. This is calculated exactly by doing abi encoding uint256 internal constant TRANSMIT_CALLDATA_FIXED_BYTES_OVERHEAD = 932; uint256 internal constant TRANSMIT_CALLDATA_PER_SIGNER_BYTES_OVERHEAD = 64; // Next block of constants are used in actual payment calculation. We calculate the exact gas used within the // tx itself, but since payment processing itself takes gas, and it needs the overhead as input, we use fixed constants // to account for gas used in payment processing. These values are calibrated using hardhat tests which simulates various cases and verifies that // the variables result in accurate estimation uint256 internal constant ACCOUNTING_FIXED_GAS_OVERHEAD = 51_200; // Fixed overhead per tx uint256 internal constant ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD = 14_200; // Overhead per upkeep performed in batch LinkTokenInterface internal immutable i_link; AggregatorV3Interface internal immutable i_linkUSDFeed; AggregatorV3Interface internal immutable i_nativeUSDFeed; AggregatorV3Interface internal immutable i_fastGasFeed; address internal immutable i_automationForwarderLogic; address internal immutable i_allowedReadOnlyAddress; IWrappedNative internal immutable i_wrappedNativeToken; /** * @dev - The storage is gas optimised for one and only one function - transmit. All the storage accessed in transmit * is stored compactly. Rest of the storage layout is not of much concern as transmit is the only hot path */ // Upkeep storage EnumerableSet.UintSet internal s_upkeepIDs; mapping(uint256 => Upkeep) internal s_upkeep; // accessed during transmit mapping(uint256 => address) internal s_upkeepAdmin; mapping(uint256 => address) internal s_proposedAdmin; mapping(uint256 => bytes) internal s_checkData; mapping(bytes32 => bool) internal s_dedupKeys; // Registry config and state EnumerableSet.AddressSet internal s_registrars; mapping(address => Transmitter) internal s_transmitters; mapping(address => Signer) internal s_signers; address[] internal s_signersList; // s_signersList contains the signing address of each oracle address[] internal s_transmittersList; // s_transmittersList contains the transmission address of each oracle EnumerableSet.AddressSet internal s_deactivatedTransmitters; mapping(address => address) internal s_transmitterPayees; // s_payees contains the mapping from transmitter to payee. mapping(address => address) internal s_proposedPayee; // proposed payee for a transmitter bytes32 internal s_latestConfigDigest; // Read on transmit path in case of signature verification HotVars internal s_hotVars; // Mixture of config and state, used in transmit Storage internal s_storage; // Mixture of config and state, not used in transmit uint256 internal s_fallbackGasPrice; uint256 internal s_fallbackLinkPrice; uint256 internal s_fallbackNativePrice; mapping(address => MigrationPermission) internal s_peerRegistryMigrationPermission; // Permissions for migration to and fro mapping(uint256 => bytes) internal s_upkeepTriggerConfig; // upkeep triggers mapping(uint256 => bytes) internal s_upkeepOffchainConfig; // general config set by users for each upkeep mapping(uint256 => bytes) internal s_upkeepPrivilegeConfig; // general config set by an administrative role for an upkeep mapping(address => bytes) internal s_adminPrivilegeConfig; // general config set by an administrative role for an admin // billing mapping(IERC20 billingToken => uint256 reserveAmount) internal s_reserveAmounts; // unspent user deposits + unwithdrawn NOP payments mapping(IERC20 billingToken => BillingConfig billingConfig) internal s_billingConfigs; // billing configurations for different tokens mapping(uint256 upkeepID => BillingOverrides billingOverrides) internal s_billingOverrides; // billing overrides for specific upkeeps IERC20[] internal s_billingTokens; // list of billing tokens PayoutMode internal s_payoutMode; error ArrayHasNoEntries(); error CannotCancel(); error CheckDataExceedsLimit(); error ConfigDigestMismatch(); error DuplicateEntry(); error DuplicateSigners(); error GasLimitCanOnlyIncrease(); error GasLimitOutsideRange(); error IncorrectNumberOfFaultyOracles(); error IncorrectNumberOfSignatures(); error IncorrectNumberOfSigners(); error IndexOutOfRange(); error InsufficientBalance(uint256 available, uint256 requested); error InsufficientLinkLiquidity(); error InvalidDataLength(); error InvalidFeed(); error InvalidTrigger(); error InvalidPayee(); error InvalidRecipient(); error InvalidReport(); error InvalidSigner(); error InvalidToken(); error InvalidTransmitter(); error InvalidTriggerType(); error MigrationNotPermitted(); error MustSettleOffchain(); error MustSettleOnchain(); error NotAContract(); error OnlyActiveSigners(); error OnlyActiveTransmitters(); error OnlyCallableByAdmin(); error OnlyCallableByLINKToken(); error OnlyCallableByOwnerOrAdmin(); error OnlyCallableByOwnerOrRegistrar(); error OnlyCallableByPayee(); error OnlyCallableByProposedAdmin(); error OnlyCallableByProposedPayee(); error OnlyCallableByUpkeepPrivilegeManager(); error OnlyFinanceAdmin(); error OnlyPausedUpkeep(); error OnlySimulatedBackend(); error OnlyUnpausedUpkeep(); error ParameterLengthError(); error ReentrantCall(); error RegistryPaused(); error RepeatedSigner(); error RepeatedTransmitter(); error TargetCheckReverted(bytes reason); error TooManyOracles(); error TranscoderNotSet(); error TransferFailed(); error UpkeepAlreadyExists(); error UpkeepCancelled(); error UpkeepNotCanceled(); error UpkeepNotNeeded(); error ValueNotChanged(); error ZeroAddressNotAllowed(); enum MigrationPermission { NONE, OUTGOING, INCOMING, BIDIRECTIONAL } enum Trigger { CONDITION, LOG } enum UpkeepFailureReason { NONE, UPKEEP_CANCELLED, UPKEEP_PAUSED, TARGET_CHECK_REVERTED, UPKEEP_NOT_NEEDED, PERFORM_DATA_EXCEEDS_LIMIT, INSUFFICIENT_BALANCE, CALLBACK_REVERTED, REVERT_DATA_EXCEEDS_LIMIT, REGISTRY_PAUSED } enum PayoutMode { ON_CHAIN, OFF_CHAIN } /** * @notice OnchainConfig of the registry * @dev used only in setConfig() * @member checkGasLimit gas limit when checking for upkeep * @member stalenessSeconds number of seconds that is allowed for feed data to * be stale before switching to the fallback pricing * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price * when calculating the payment ceiling for keepers * @member maxPerformGas max performGas allowed for an upkeep on this registry * @member maxCheckDataSize max length of checkData bytes * @member maxPerformDataSize max length of performData bytes * @member maxRevertDataSize max length of revertData bytes * @member fallbackGasPrice gas price used if the gas price feed is stale * @member fallbackLinkPrice LINK price used if the LINK price feed is stale * @member transcoder address of the transcoder contract * @member registrars addresses of the registrar contracts * @member upkeepPrivilegeManager address which can set privilege for upkeeps * @member reorgProtectionEnabled if this registry enables re-org protection checks * @member chainModule the chain specific module */ struct OnchainConfig { uint32 checkGasLimit; uint32 maxPerformGas; uint32 maxCheckDataSize; address transcoder; // 1 word full bool reorgProtectionEnabled; uint24 stalenessSeconds; uint32 maxPerformDataSize; uint32 maxRevertDataSize; address upkeepPrivilegeManager; // 2 words full uint16 gasCeilingMultiplier; address financeAdmin; // 3 words uint256 fallbackGasPrice; uint256 fallbackLinkPrice; uint256 fallbackNativePrice; address[] registrars; IChainModule chainModule; } /** * @notice relevant state of an upkeep which is used in transmit function * @member paused if this upkeep has been paused * @member overridesEnabled if this upkeep has overrides enabled * @member performGas the gas limit of upkeep execution * @member maxValidBlocknumber until which block this upkeep is valid * @member forwarder the forwarder contract to use for this upkeep * @member amountSpent the amount this upkeep has spent, in the upkeep's billing token * @member balance the balance of this upkeep * @member lastPerformedBlockNumber the last block number when this upkeep was performed */ struct Upkeep { bool paused; bool overridesEnabled; uint32 performGas; uint32 maxValidBlocknumber; IAutomationForwarder forwarder; // 2 bytes left in 1st EVM word - read in transmit path uint128 amountSpent; uint96 balance; uint32 lastPerformedBlockNumber; // 0 bytes left in 2nd EVM word - written in transmit path IERC20 billingToken; // 12 bytes left in 3rd EVM word - read in transmit path } /// @dev Config + State storage struct which is on hot transmit path struct HotVars { uint96 totalPremium; // ─────────╮ total historical payment to oracles for premium uint32 latestEpoch; // │ latest epoch for which a report was transmitted uint24 stalenessSeconds; // │ Staleness tolerance for feeds uint16 gasCeilingMultiplier; // │ multiplier on top of fast gas feed for upper bound uint8 f; // │ maximum number of faulty oracles bool paused; // │ pause switch for all upkeeps in the registry bool reentrancyGuard; // | guard against reentrancy bool reorgProtectionEnabled; // ─╯ if this registry should enable the re-org protection mechanism IChainModule chainModule; // the interface of chain specific module } /// @dev Config + State storage struct which is not on hot transmit path struct Storage { address transcoder; // Address of transcoder contract used in migrations uint32 checkGasLimit; // Gas limit allowed in checkUpkeep uint32 maxPerformGas; // Max gas an upkeep can use on this registry uint32 nonce; // Nonce for each upkeep created // 1 EVM word full address upkeepPrivilegeManager; // address which can set privilege for upkeeps uint32 configCount; // incremented each time a new config is posted, The count is incorporated into the config digest to prevent replay attacks. uint32 latestConfigBlockNumber; // makes it easier for offchain systems to extract config from logs uint32 maxCheckDataSize; // max length of checkData bytes // 2 EVM word full address financeAdmin; // address which can withdraw funds from the contract uint32 maxPerformDataSize; // max length of performData bytes uint32 maxRevertDataSize; // max length of revertData bytes // 4 bytes left in 3rd EVM word } /// @dev Report transmitted by OCR to transmit function struct Report { uint256 fastGasWei; uint256 linkUSD; uint256[] upkeepIds; uint256[] gasLimits; bytes[] triggers; bytes[] performDatas; } /** * @dev This struct is used to maintain run time information about an upkeep in transmit function * @member upkeep the upkeep struct * @member earlyChecksPassed whether the upkeep passed early checks before perform * @member performSuccess whether the perform was successful * @member triggerType the type of trigger * @member gasUsed gasUsed by this upkeep in perform * @member calldataWeight weight assigned to this upkeep for its contribution to calldata. It is used to split L1 fee * @member dedupID unique ID used to dedup an upkeep/trigger combo */ struct UpkeepTransmitInfo { Upkeep upkeep; bool earlyChecksPassed; bool performSuccess; Trigger triggerType; uint256 gasUsed; uint256 calldataWeight; bytes32 dedupID; } /** * @notice holds information about a transmiter / node in the DON * @member active can this transmitter submit reports * @member index of oracle in s_signersList/s_transmittersList * @member balance a node's balance in LINK * @member lastCollected the total balance at which the node last withdrew * @dev uint96 is safe for balance / last collected because transmitters are only ever paid in LINK */ struct Transmitter { bool active; uint8 index; uint96 balance; uint96 lastCollected; } struct TransmitterPayeeInfo { address transmitterAddress; address payeeAddress; } struct Signer { bool active; // Index of oracle in s_signersList/s_transmittersList uint8 index; } /** * @notice the trigger structure conditional trigger type */ struct ConditionalTrigger { uint32 blockNum; bytes32 blockHash; } /** * @notice the trigger structure of log upkeeps * @dev NOTE that blockNum / blockHash describe the block used for the callback, * not necessarily the block number that the log was emitted in!!!! */ struct LogTrigger { bytes32 logBlockHash; bytes32 txHash; uint32 logIndex; uint32 blockNum; bytes32 blockHash; } /** * @notice the billing config of a token * @dev this is a storage struct */ // solhint-disable-next-line gas-struct-packing struct BillingConfig { uint32 gasFeePPB; uint24 flatFeeMilliCents; // min fee is $0.00001, max fee is $167 AggregatorV3Interface priceFeed; uint8 decimals; // 1st word, read in calculating BillingTokenPaymentParams uint256 fallbackPrice; // 2nd word only read if stale uint96 minSpend; // 3rd word only read during cancellation } /** * @notice override-able billing params of a billing token */ struct BillingOverrides { uint32 gasFeePPB; uint24 flatFeeMilliCents; } /** * @notice pricing params for a billing token * @dev this is a memory-only struct, so struct packing is less important */ struct BillingTokenPaymentParams { uint8 decimals; uint32 gasFeePPB; uint24 flatFeeMilliCents; uint256 priceUSD; } /** * @notice struct containing price & payment information used in calculating payment amount * @member gasLimit the amount of gas used * @member gasOverhead the amount of gas overhead * @member l1CostWei the amount to be charged for L1 fee in wei * @member fastGasWei the fast gas price * @member linkUSD the exchange ratio between LINK and USD * @member nativeUSD the exchange ratio between the chain's native token and USD * @member billingToken the billing token * @member billingTokenParams the payment params specific to a particular payment token * @member isTransaction is this an eth_call or a transaction */ struct PaymentParams { uint256 gasLimit; uint256 gasOverhead; uint256 l1CostWei; uint256 fastGasWei; uint256 linkUSD; uint256 nativeUSD; IERC20 billingToken; BillingTokenPaymentParams billingTokenParams; bool isTransaction; } /** * @notice struct containing receipt information about a payment or cost estimation * @member gasChargeInBillingToken the amount to charge a user for gas spent using the billing token's native decimals * @member premiumInBillingToken the premium charged to the user, shared between all nodes, using the billing token's native decimals * @member gasReimbursementInJuels the amount to reimburse a node for gas spent * @member premiumInJuels the premium paid to NOPs, shared between all nodes */ // solhint-disable-next-line gas-struct-packing struct PaymentReceipt { uint96 gasChargeInBillingToken; uint96 premiumInBillingToken; // one word ends uint96 gasReimbursementInJuels; uint96 premiumInJuels; // second word ends IERC20 billingToken; uint96 linkUSD; // third word ends uint96 nativeUSD; uint96 billingUSD; // fourth word ends } event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig); event BillingConfigOverridden(uint256 indexed id, BillingOverrides overrides); event BillingConfigOverrideRemoved(uint256 indexed id); event BillingConfigSet(IERC20 indexed token, BillingConfig config); event CancelledUpkeepReport(uint256 indexed id, bytes trigger); event ChainSpecificModuleUpdated(address newModule); event DedupKeyAdded(bytes32 indexed dedupKey); event FeesWithdrawn(address indexed assetAddress, address indexed recipient, uint256 amount); event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); event InsufficientFundsUpkeepReport(uint256 indexed id, bytes trigger); event NOPsSettledOffchain(address[] payees, uint256[] payments); event Paused(address account); event PayeesUpdated(address[] transmitters, address[] payees); event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); event PaymentWithdrawn(address indexed transmitter, uint256 indexed amount, address indexed to, address payee); event ReorgedUpkeepReport(uint256 indexed id, bytes trigger); event StaleUpkeepReport(uint256 indexed id, bytes trigger); event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); event UpkeepPaused(uint256 indexed id); event UpkeepPerformed( uint256 indexed id, bool indexed success, uint96 totalPayment, uint256 gasUsed, uint256 gasOverhead, bytes trigger ); event UpkeepCharged(uint256 indexed id, PaymentReceipt receipt); event UpkeepPrivilegeConfigSet(uint256 indexed id, bytes privilegeConfig); event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); event UpkeepRegistered(uint256 indexed id, uint32 performGas, address admin); event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); event UpkeepUnpaused(uint256 indexed id); event Unpaused(address account); /** * @param link address of the LINK Token * @param linkUSDFeed address of the LINK/USD price feed * @param nativeUSDFeed address of the Native/USD price feed * @param fastGasFeed address of the Fast Gas price feed * @param automationForwarderLogic the address of automation forwarder logic * @param allowedReadOnlyAddress the address of the allowed read only address * @param payoutMode the payout mode */ constructor( address link, address linkUSDFeed, address nativeUSDFeed, address fastGasFeed, address automationForwarderLogic, address allowedReadOnlyAddress, PayoutMode payoutMode, address wrappedNativeTokenAddress ) ConfirmedOwner(msg.sender) { i_link = LinkTokenInterface(link); i_linkUSDFeed = AggregatorV3Interface(linkUSDFeed); i_nativeUSDFeed = AggregatorV3Interface(nativeUSDFeed); i_fastGasFeed = AggregatorV3Interface(fastGasFeed); i_automationForwarderLogic = automationForwarderLogic; i_allowedReadOnlyAddress = allowedReadOnlyAddress; s_payoutMode = payoutMode; i_wrappedNativeToken = IWrappedNative(wrappedNativeTokenAddress); if (i_linkUSDFeed.decimals() != i_nativeUSDFeed.decimals()) { revert InvalidFeed(); } } // ================================================================ // | INTERNAL FUNCTIONS ONLY | // ================================================================ /** * @dev creates a new upkeep with the given fields * @param id the id of the upkeep * @param upkeep the upkeep to create * @param admin address to cancel upkeep and withdraw remaining funds * @param checkData data which is passed to user's checkUpkeep * @param triggerConfig the trigger config for this upkeep * @param offchainConfig the off-chain config of this upkeep */ function _createUpkeep( uint256 id, Upkeep memory upkeep, address admin, bytes memory checkData, bytes memory triggerConfig, bytes memory offchainConfig ) internal { if (s_hotVars.paused) revert RegistryPaused(); if (checkData.length > s_storage.maxCheckDataSize) revert CheckDataExceedsLimit(); if (upkeep.performGas < PERFORM_GAS_MIN || upkeep.performGas > s_storage.maxPerformGas) revert GasLimitOutsideRange(); if (address(s_upkeep[id].forwarder) != address(0)) revert UpkeepAlreadyExists(); if (address(s_billingConfigs[upkeep.billingToken].priceFeed) == address(0)) revert InvalidToken(); s_upkeep[id] = upkeep; s_upkeepAdmin[id] = admin; s_checkData[id] = checkData; s_reserveAmounts[upkeep.billingToken] = s_reserveAmounts[upkeep.billingToken] + upkeep.balance; s_upkeepTriggerConfig[id] = triggerConfig; s_upkeepOffchainConfig[id] = offchainConfig; s_upkeepIDs.add(id); } /** * @dev creates an ID for the upkeep based on the upkeep's type * @dev the format of the ID looks like this: * ****00000000000X**************** * 4 bytes of entropy * 11 bytes of zeros * 1 identifying byte for the trigger type * 16 bytes of entropy * @dev this maintains the same level of entropy as eth addresses, so IDs will still be unique * @dev we add the "identifying" part in the middle so that it is mostly hidden from users who usually only * see the first 4 and last 4 hex values ex 0x1234...ABCD */ function _createID(Trigger triggerType) internal view returns (uint256) { bytes1 empty; IChainModule chainModule = s_hotVars.chainModule; bytes memory idBytes = abi.encodePacked( keccak256(abi.encode(chainModule.blockHash((chainModule.blockNumber() - 1)), address(this), s_storage.nonce)) ); for (uint256 idx = 4; idx < 15; idx++) { idBytes[idx] = empty; } idBytes[15] = bytes1(uint8(triggerType)); return uint256(bytes32(idBytes)); } /** * @dev retrieves feed data for fast gas/native and link/native prices. if the feed * data is stale it uses the configured fallback price. Once a price is picked * for gas it takes the min of gas price in the transaction or the fast gas * price in order to reduce costs for the upkeep clients. */ function _getFeedData( HotVars memory hotVars ) internal view returns (uint256 gasWei, uint256 linkUSD, uint256 nativeUSD) { uint32 stalenessSeconds = hotVars.stalenessSeconds; bool staleFallback = stalenessSeconds > 0; uint256 timestamp; int256 feedValue; (, feedValue, , timestamp, ) = i_fastGasFeed.latestRoundData(); if ( feedValue <= 0 || block.timestamp < timestamp || (staleFallback && stalenessSeconds < block.timestamp - timestamp) ) { gasWei = s_fallbackGasPrice; } else { gasWei = uint256(feedValue); } (, feedValue, , timestamp, ) = i_linkUSDFeed.latestRoundData(); if ( feedValue <= 0 || block.timestamp < timestamp || (staleFallback && stalenessSeconds < block.timestamp - timestamp) ) { linkUSD = s_fallbackLinkPrice; } else { linkUSD = uint256(feedValue); } return (gasWei, linkUSD, _getNativeUSD(hotVars)); } /** * @dev this price has it's own getter for use in the transmit() hot path * in the future, all price data should be included in the report instead of * getting read during execution */ function _getNativeUSD(HotVars memory hotVars) internal view returns (uint256) { (, int256 feedValue, , uint256 timestamp, ) = i_nativeUSDFeed.latestRoundData(); if ( feedValue <= 0 || block.timestamp < timestamp || (hotVars.stalenessSeconds > 0 && hotVars.stalenessSeconds < block.timestamp - timestamp) ) { return s_fallbackNativePrice; } else { return uint256(feedValue); } } /** * @dev gets the price and billing params for a specific billing token */ function _getBillingTokenPaymentParams( HotVars memory hotVars, IERC20 billingToken ) internal view returns (BillingTokenPaymentParams memory paymentParams) { BillingConfig storage config = s_billingConfigs[billingToken]; paymentParams.flatFeeMilliCents = config.flatFeeMilliCents; paymentParams.gasFeePPB = config.gasFeePPB; paymentParams.decimals = config.decimals; (, int256 feedValue, , uint256 timestamp, ) = config.priceFeed.latestRoundData(); if ( feedValue <= 0 || block.timestamp < timestamp || (hotVars.stalenessSeconds > 0 && hotVars.stalenessSeconds < block.timestamp - timestamp) ) { paymentParams.priceUSD = config.fallbackPrice; } else { paymentParams.priceUSD = uint256(feedValue); } return paymentParams; } /** * @param hotVars the hot path variables * @param paymentParams the pricing data and gas usage data * @return receipt the receipt of payment with pricing breakdown * @dev use of PaymentParams struct is necessary to avoid stack too deep errors * @dev calculates LINK paid for gas spent plus a configure premium percentage * @dev 1 USD = 1e18 attoUSD * @dev 1 USD = 1e26 hexaicosaUSD (had to borrow this prefix from geometry because there is no metric prefix for 1e-26) * @dev 1 millicent = 1e-5 USD = 1e13 attoUSD */ function _calculatePaymentAmount( HotVars memory hotVars, PaymentParams memory paymentParams ) internal view returns (PaymentReceipt memory receipt) { uint256 decimals = paymentParams.billingTokenParams.decimals; uint256 gasWei = paymentParams.fastGasWei * hotVars.gasCeilingMultiplier; // in case it's actual execution use actual gas price, capped by fastGasWei * gasCeilingMultiplier if (paymentParams.isTransaction && tx.gasprice < gasWei) { gasWei = tx.gasprice; } // scaling factor is based on decimals of billing token, and applies to premium and gasCharge uint256 numeratorScalingFactor = decimals > 18 ? 10 ** (decimals - 18) : 1; uint256 denominatorScalingFactor = decimals < 18 ? 10 ** (18 - decimals) : 1; // gas calculation uint256 gasPaymentHexaicosaUSD = (gasWei * (paymentParams.gasLimit + paymentParams.gasOverhead) + paymentParams.l1CostWei) * paymentParams.nativeUSD; // gasPaymentHexaicosaUSD has an extra 8 zeros because of decimals on nativeUSD feed // gasChargeInBillingToken is scaled by the billing token's decimals. Round up to ensure a minimum billing token is charged for gas receipt.gasChargeInBillingToken = SafeCast.toUint96( ((gasPaymentHexaicosaUSD * numeratorScalingFactor) + (paymentParams.billingTokenParams.priceUSD * denominatorScalingFactor - 1)) / (paymentParams.billingTokenParams.priceUSD * denominatorScalingFactor) ); // 18 decimals: 26 decimals / 8 decimals receipt.gasReimbursementInJuels = SafeCast.toUint96(gasPaymentHexaicosaUSD / paymentParams.linkUSD); // premium calculation uint256 flatFeeHexaicosaUSD = uint256(paymentParams.billingTokenParams.flatFeeMilliCents) * 1e21; // 1e13 for milliCents to attoUSD and 1e8 for attoUSD to hexaicosaUSD uint256 premiumHexaicosaUSD = ((((gasWei * paymentParams.gasLimit) + paymentParams.l1CostWei) * paymentParams.billingTokenParams.gasFeePPB * paymentParams.nativeUSD) / 1e9) + flatFeeHexaicosaUSD; // premium is scaled by the billing token's decimals. Round up to ensure at least minimum charge receipt.premiumInBillingToken = SafeCast.toUint96( ((premiumHexaicosaUSD * numeratorScalingFactor) + (paymentParams.billingTokenParams.priceUSD * denominatorScalingFactor - 1)) / (paymentParams.billingTokenParams.priceUSD * denominatorScalingFactor) ); receipt.premiumInJuels = SafeCast.toUint96(premiumHexaicosaUSD / paymentParams.linkUSD); receipt.billingToken = paymentParams.billingToken; receipt.linkUSD = SafeCast.toUint96(paymentParams.linkUSD); receipt.nativeUSD = SafeCast.toUint96(paymentParams.nativeUSD); receipt.billingUSD = SafeCast.toUint96(paymentParams.billingTokenParams.priceUSD); return receipt; } /** * @dev calculates the max payment for an upkeep. Called during checkUpkeep simulation and assumes * maximum gas overhead, L1 fee */ function _getMaxPayment( uint256 upkeepId, HotVars memory hotVars, Trigger triggerType, uint32 performGas, uint256 fastGasWei, uint256 linkUSD, uint256 nativeUSD, IERC20 billingToken ) internal view returns (uint96) { uint256 maxL1Fee; uint256 maxGasOverhead; { if (triggerType == Trigger.CONDITION) { maxGasOverhead = REGISTRY_CONDITIONAL_OVERHEAD; } else if (triggerType == Trigger.LOG) { maxGasOverhead = REGISTRY_LOG_OVERHEAD; } else { revert InvalidTriggerType(); } uint256 maxCalldataSize = s_storage.maxPerformDataSize + TRANSMIT_CALLDATA_FIXED_BYTES_OVERHEAD + (TRANSMIT_CALLDATA_PER_SIGNER_BYTES_OVERHEAD * (hotVars.f + 1)); (uint256 chainModuleFixedOverhead, uint256 chainModulePerByteOverhead) = s_hotVars.chainModule.getGasOverhead(); maxGasOverhead += (REGISTRY_PER_SIGNER_GAS_OVERHEAD * (hotVars.f + 1)) + ((REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD + chainModulePerByteOverhead) * maxCalldataSize) + chainModuleFixedOverhead; maxL1Fee = hotVars.gasCeilingMultiplier * hotVars.chainModule.getMaxL1Fee(maxCalldataSize); } BillingTokenPaymentParams memory paymentParams = _getBillingTokenPaymentParams(hotVars, billingToken); if (s_upkeep[upkeepId].overridesEnabled) { BillingOverrides memory billingOverrides = s_billingOverrides[upkeepId]; // use the overridden configs paymentParams.gasFeePPB = billingOverrides.gasFeePPB; paymentParams.flatFeeMilliCents = billingOverrides.flatFeeMilliCents; } PaymentReceipt memory receipt = _calculatePaymentAmount( hotVars, PaymentParams({ gasLimit: performGas, gasOverhead: maxGasOverhead, l1CostWei: maxL1Fee, fastGasWei: fastGasWei, linkUSD: linkUSD, nativeUSD: nativeUSD, billingToken: billingToken, billingTokenParams: paymentParams, isTransaction: false }) ); return receipt.gasChargeInBillingToken + receipt.premiumInBillingToken; } /** * @dev move a transmitter's balance from total pool to withdrawable balance */ function _updateTransmitterBalanceFromPool( address transmitterAddress, uint96 totalPremium, uint96 payeeCount ) internal returns (uint96) { Transmitter memory transmitter = s_transmitters[transmitterAddress]; if (transmitter.active) { uint96 uncollected = totalPremium - transmitter.lastCollected; uint96 due = uncollected / payeeCount; transmitter.balance += due; transmitter.lastCollected += due * payeeCount; s_transmitters[transmitterAddress] = transmitter; } return transmitter.balance; } /** * @dev gets the trigger type from an upkeepID (trigger type is encoded in the middle of the ID) */ function _getTriggerType(uint256 upkeepId) internal pure returns (Trigger) { bytes32 rawID = bytes32(upkeepId); bytes1 empty = bytes1(0); for (uint256 idx = 4; idx < 15; idx++) { if (rawID[idx] != empty) { // old IDs that were created before this standard and migrated to this registry return Trigger.CONDITION; } } return Trigger(uint8(rawID[15])); } function _checkPayload( uint256 upkeepId, Trigger triggerType, bytes memory triggerData ) internal view returns (bytes memory) { if (triggerType == Trigger.CONDITION) { return abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[upkeepId]); } else if (triggerType == Trigger.LOG) { Log memory log = abi.decode(triggerData, (Log)); return abi.encodeWithSelector(CHECK_LOG_SELECTOR, log, s_checkData[upkeepId]); } revert InvalidTriggerType(); } /** * @dev _decodeReport decodes a serialized report into a Report struct */ function _decodeReport(bytes calldata rawReport) internal pure returns (Report memory) { Report memory report = abi.decode(rawReport, (Report)); uint256 expectedLength = report.upkeepIds.length; if ( report.gasLimits.length != expectedLength || report.triggers.length != expectedLength || report.performDatas.length != expectedLength ) { revert InvalidReport(); } return report; } /** * @dev Does some early sanity checks before actually performing an upkeep * @return bool whether the upkeep should be performed * @return bytes32 dedupID for preventing duplicate performances of this trigger */ function _prePerformChecks( uint256 upkeepId, uint256 blocknumber, bytes memory rawTrigger, UpkeepTransmitInfo memory transmitInfo, HotVars memory hotVars ) internal returns (bool, bytes32) { bytes32 dedupID; if (transmitInfo.triggerType == Trigger.CONDITION) { if (!_validateConditionalTrigger(upkeepId, blocknumber, rawTrigger, transmitInfo, hotVars)) return (false, dedupID); } else if (transmitInfo.triggerType == Trigger.LOG) { bool valid; (valid, dedupID) = _validateLogTrigger(upkeepId, blocknumber, rawTrigger, hotVars); if (!valid) return (false, dedupID); } else { revert InvalidTriggerType(); } if (transmitInfo.upkeep.maxValidBlocknumber <= blocknumber) { // Can happen when an upkeep got cancelled after report was generated. // However we have a CANCELLATION_DELAY of 50 blocks so shouldn't happen in practice emit CancelledUpkeepReport(upkeepId, rawTrigger); return (false, dedupID); } return (true, dedupID); } /** * @dev Does some early sanity checks before actually performing an upkeep */ function _validateConditionalTrigger( uint256 upkeepId, uint256 blocknumber, bytes memory rawTrigger, UpkeepTransmitInfo memory transmitInfo, HotVars memory hotVars ) internal returns (bool) { ConditionalTrigger memory trigger = abi.decode(rawTrigger, (ConditionalTrigger)); if (trigger.blockNum < transmitInfo.upkeep.lastPerformedBlockNumber) { // Can happen when another report performed this upkeep after this report was generated emit StaleUpkeepReport(upkeepId, rawTrigger); return false; } if ( (hotVars.reorgProtectionEnabled && (trigger.blockHash != bytes32("") && hotVars.chainModule.blockHash(trigger.blockNum) != trigger.blockHash)) || trigger.blockNum >= blocknumber ) { // There are two cases of reorged report // 1. trigger block number is in future: this is an edge case during extreme deep reorgs of chain // which is always protected against // 2. blockHash at trigger block number was same as trigger time. This is an optional check which is // applied if DON sends non empty trigger.blockHash. Note: It only works for last 256 blocks on chain // when it is sent emit ReorgedUpkeepReport(upkeepId, rawTrigger); return false; } return true; } function _validateLogTrigger( uint256 upkeepId, uint256 blocknumber, bytes memory rawTrigger, HotVars memory hotVars ) internal returns (bool, bytes32) { LogTrigger memory trigger = abi.decode(rawTrigger, (LogTrigger)); bytes32 dedupID = keccak256(abi.encodePacked(upkeepId, trigger.logBlockHash, trigger.txHash, trigger.logIndex)); if ( (hotVars.reorgProtectionEnabled && (trigger.blockHash != bytes32("") && hotVars.chainModule.blockHash(trigger.blockNum) != trigger.blockHash)) || trigger.blockNum >= blocknumber ) { // Reorg protection is same as conditional trigger upkeeps emit ReorgedUpkeepReport(upkeepId, rawTrigger); return (false, dedupID); } if (s_dedupKeys[dedupID]) { emit StaleUpkeepReport(upkeepId, rawTrigger); return (false, dedupID); } return (true, dedupID); } /** * @dev Verify signatures attached to report */ function _verifyReportSignature( bytes32[3] calldata reportContext, bytes calldata report, bytes32[] calldata rs, bytes32[] calldata ss, bytes32 rawVs ) internal view { bytes32 h = keccak256(abi.encode(keccak256(report), reportContext)); // i-th byte counts number of sigs made by i-th signer uint256 signedCount = 0; Signer memory signer; address signerAddress; for (uint256 i = 0; i < rs.length; i++) { signerAddress = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); signer = s_signers[signerAddress]; if (!signer.active) revert OnlyActiveSigners(); unchecked { signedCount += 1 << (8 * signer.index); } } if (signedCount & ORACLE_MASK != signedCount) revert DuplicateSigners(); } /** * @dev updates a storage marker for this upkeep to prevent duplicate and out of order performances * @dev for conditional triggers we set the latest block number, for log triggers we store a dedupID */ function _updateTriggerMarker( uint256 upkeepID, uint256 blocknumber, UpkeepTransmitInfo memory upkeepTransmitInfo ) internal { if (upkeepTransmitInfo.triggerType == Trigger.CONDITION) { s_upkeep[upkeepID].lastPerformedBlockNumber = uint32(blocknumber); } else if (upkeepTransmitInfo.triggerType == Trigger.LOG) { s_dedupKeys[upkeepTransmitInfo.dedupID] = true; emit DedupKeyAdded(upkeepTransmitInfo.dedupID); } } /** * @dev calls the Upkeep target with the performData param passed in by the * transmitter and the exact gas required by the Upkeep */ function _performUpkeep( IAutomationForwarder forwarder, uint256 performGas, bytes memory performData ) internal nonReentrant returns (bool success, uint256 gasUsed) { performData = abi.encodeWithSelector(PERFORM_SELECTOR, performData); return forwarder.forward(performGas, performData); } /** * @dev handles the payment processing after an upkeep has been performed. * Deducts an upkeep's balance and increases the amount spent. */ function _handlePayment( HotVars memory hotVars, PaymentParams memory paymentParams, uint256 upkeepId, Upkeep memory upkeep ) internal returns (PaymentReceipt memory) { if (upkeep.overridesEnabled) { BillingOverrides memory billingOverrides = s_billingOverrides[upkeepId]; // use the overridden configs paymentParams.billingTokenParams.gasFeePPB = billingOverrides.gasFeePPB; paymentParams.billingTokenParams.flatFeeMilliCents = billingOverrides.flatFeeMilliCents; } PaymentReceipt memory receipt = _calculatePaymentAmount(hotVars, paymentParams); // balance is in the token's native decimals uint96 balance = upkeep.balance; // payment is in the token's native decimals uint96 payment = receipt.gasChargeInBillingToken + receipt.premiumInBillingToken; // scaling factors to adjust decimals between billing token and LINK uint256 decimals = paymentParams.billingTokenParams.decimals; uint256 scalingFactor1 = decimals < 18 ? 10 ** (18 - decimals) : 1; uint256 scalingFactor2 = decimals > 18 ? 10 ** (decimals - 18) : 1; // this shouldn't happen, but in rare edge cases, we charge the full balance in case the user // can't cover the amount owed if (balance < receipt.gasChargeInBillingToken) { // if the user can't cover the gas fee, then direct all of the payment to the transmitter and distribute no premium to the DON payment = balance; receipt.gasReimbursementInJuels = SafeCast.toUint96( (balance * paymentParams.billingTokenParams.priceUSD * scalingFactor1) / (paymentParams.linkUSD * scalingFactor2) ); receipt.premiumInJuels = 0; receipt.premiumInBillingToken = 0; receipt.gasChargeInBillingToken = balance; } else if (balance < payment) { // if the user can cover the gas fee, but not the premium, then reduce the premium payment = balance; receipt.premiumInJuels = SafeCast.toUint96( ((balance * paymentParams.billingTokenParams.priceUSD * scalingFactor1) / (paymentParams.linkUSD * scalingFactor2)) - receipt.gasReimbursementInJuels ); // round up receipt.premiumInBillingToken = SafeCast.toUint96( ((receipt.premiumInJuels * paymentParams.linkUSD * scalingFactor2) + (paymentParams.billingTokenParams.priceUSD * scalingFactor1 - 1)) / (paymentParams.billingTokenParams.priceUSD * scalingFactor1) ); } s_upkeep[upkeepId].balance -= payment; s_upkeep[upkeepId].amountSpent += payment; s_reserveAmounts[paymentParams.billingToken] -= payment; emit UpkeepCharged(upkeepId, receipt); return receipt; } /** * @dev ensures the upkeep is not cancelled and the caller is the upkeep admin */ function _requireAdminAndNotCancelled(uint256 upkeepId) internal view { if (msg.sender != s_upkeepAdmin[upkeepId]) revert OnlyCallableByAdmin(); if (s_upkeep[upkeepId].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); } /** * @dev replicates Open Zeppelin's ReentrancyGuard but optimized to fit our storage */ modifier nonReentrant() { if (s_hotVars.reentrancyGuard) revert ReentrantCall(); s_hotVars.reentrancyGuard = true; _; s_hotVars.reentrancyGuard = false; } /** * @notice only allows a pre-configured address to initiate offchain read */ function _preventExecution() internal view { // solhint-disable-next-line avoid-tx-origin if (tx.origin != i_allowedReadOnlyAddress) { revert OnlySimulatedBackend(); } } /** * @notice only allows finance admin to call the function */ function _onlyFinanceAdminAllowed() internal view { if (msg.sender != s_storage.financeAdmin) { revert OnlyFinanceAdmin(); } } /** * @notice only allows privilege manager to call the function */ function _onlyPrivilegeManagerAllowed() internal view { if (msg.sender != s_storage.upkeepPrivilegeManager) { revert OnlyCallableByUpkeepPrivilegeManager(); } } /** * @notice sets billing configuration for a token * @param billingTokens the addresses of tokens * @param billingConfigs the configs for tokens */ function _setBillingConfig(IERC20[] memory billingTokens, BillingConfig[] memory billingConfigs) internal { // Clear existing data for (uint256 i = 0; i < s_billingTokens.length; i++) { delete s_billingConfigs[s_billingTokens[i]]; } delete s_billingTokens; PayoutMode mode = s_payoutMode; for (uint256 i = 0; i < billingTokens.length; i++) { IERC20 token = billingTokens[i]; BillingConfig memory config = billingConfigs[i]; // most ERC20 tokens are 18 decimals, priceFeed must be 8 decimals if (config.decimals != token.decimals() || config.priceFeed.decimals() != 8) { revert InvalidToken(); } // if LINK is a billing option, payout mode must be ON_CHAIN if (address(token) == address(i_link) && mode == PayoutMode.OFF_CHAIN) { revert InvalidToken(); } if (address(token) == ZERO_ADDRESS || address(config.priceFeed) == ZERO_ADDRESS) { revert ZeroAddressNotAllowed(); } // if this is a new token, add it to tokens list. Otherwise revert if (address(s_billingConfigs[token].priceFeed) != ZERO_ADDRESS) { revert DuplicateEntry(); } s_billingTokens.push(token); // update the billing config for an existing token or add a new one s_billingConfigs[token] = config; emit BillingConfigSet(token, config); } } /** * @notice updates the signers and transmitters lists */ function _updateTransmitters(address[] memory signers, address[] memory transmitters) internal { uint96 transmittersListLength = uint96(s_transmittersList.length); uint96 totalPremium = s_hotVars.totalPremium; // move all pooled payments out of the pool to each transmitter's balance for (uint256 i = 0; i < s_transmittersList.length; i++) { _updateTransmitterBalanceFromPool(s_transmittersList[i], totalPremium, transmittersListLength); } // remove any old signer/transmitter addresses address transmitterAddress; PayoutMode mode = s_payoutMode; for (uint256 i = 0; i < s_transmittersList.length; i++) { transmitterAddress = s_transmittersList[i]; delete s_signers[s_signersList[i]]; // Do not delete the whole transmitter struct as it has balance information stored s_transmitters[transmitterAddress].active = false; if (mode == PayoutMode.OFF_CHAIN && s_transmitters[transmitterAddress].balance > 0) { s_deactivatedTransmitters.add(transmitterAddress); } } delete s_signersList; delete s_transmittersList; // add new signer/transmitter addresses Transmitter memory transmitter; for (uint256 i = 0; i < signers.length; i++) { if (s_signers[signers[i]].active) revert RepeatedSigner(); if (signers[i] == ZERO_ADDRESS) revert InvalidSigner(); s_signers[signers[i]] = Signer({active: true, index: uint8(i)}); transmitterAddress = transmitters[i]; if (transmitterAddress == ZERO_ADDRESS) revert InvalidTransmitter(); transmitter = s_transmitters[transmitterAddress]; if (transmitter.active) revert RepeatedTransmitter(); transmitter.active = true; transmitter.index = uint8(i); // new transmitters start afresh from current totalPremium // some spare change of premium from previous pool will be forfeited transmitter.lastCollected = s_hotVars.totalPremium; s_transmitters[transmitterAddress] = transmitter; if (mode == PayoutMode.OFF_CHAIN) { s_deactivatedTransmitters.remove(transmitterAddress); } } s_signersList = signers; s_transmittersList = transmitters; } /** * @notice returns the size of the LINK liquidity pool # @dev LINK max supply < 2^96, so casting to int256 is safe */ function _linkAvailableForPayment() internal view returns (int256) { return int256(i_link.balanceOf(address(this))) - int256(s_reserveAmounts[IERC20(address(i_link))]); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; import {AutomationRegistryBase2_3} from "./AutomationRegistryBase2_3.sol"; import {AutomationRegistryLogicC2_3} from "./AutomationRegistryLogicC2_3.sol"; import {AutomationRegistryLogicB2_3} from "./AutomationRegistryLogicB2_3.sol"; import {Chainable} from "../Chainable.sol"; import {AutomationForwarder} from "../AutomationForwarder.sol"; import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; import {UpkeepTranscoderInterfaceV2} from "../interfaces/UpkeepTranscoderInterfaceV2.sol"; import {MigratableKeeperRegistryInterfaceV2} from "../interfaces/MigratableKeeperRegistryInterfaceV2.sol"; import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol"; /** * @notice Logic contract, works in tandem with AutomationRegistry as a proxy */ contract AutomationRegistryLogicA2_3 is AutomationRegistryBase2_3, Chainable, IERC677Receiver { using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; using SafeERC20 for IERC20; /** * @param logicB the address of the second logic contract * @dev we cast the contract to logicC in order to call logicC functions (via fallback) */ constructor( AutomationRegistryLogicB2_3 logicB ) AutomationRegistryBase2_3( AutomationRegistryLogicC2_3(address(logicB)).getLinkAddress(), AutomationRegistryLogicC2_3(address(logicB)).getLinkUSDFeedAddress(), AutomationRegistryLogicC2_3(address(logicB)).getNativeUSDFeedAddress(), AutomationRegistryLogicC2_3(address(logicB)).getFastGasFeedAddress(), AutomationRegistryLogicC2_3(address(logicB)).getAutomationForwarderLogic(), AutomationRegistryLogicC2_3(address(logicB)).getAllowedReadOnlyAddress(), AutomationRegistryLogicC2_3(address(logicB)).getPayoutMode(), AutomationRegistryLogicC2_3(address(logicB)).getWrappedNativeTokenAddress() ) Chainable(address(logicB)) {} /** * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX * @param sender the account which transferred the funds * @param amount number of LINK transfer */ function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external override { if (msg.sender != address(i_link)) revert OnlyCallableByLINKToken(); if (data.length != 32) revert InvalidDataLength(); uint256 id = abi.decode(data, (uint256)); if (s_upkeep[id].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); if (address(s_upkeep[id].billingToken) != address(i_link)) revert InvalidToken(); s_upkeep[id].balance = s_upkeep[id].balance + uint96(amount); s_reserveAmounts[IERC20(address(i_link))] = s_reserveAmounts[IERC20(address(i_link))] + amount; emit FundsAdded(id, sender, uint96(amount)); } // ================================================================ // | UPKEEP MANAGEMENT | // ================================================================ /** * @notice adds a new upkeep * @param target address to perform upkeep on * @param gasLimit amount of gas to provide the target contract when * performing upkeep * @param admin address to cancel upkeep and withdraw remaining funds * @param triggerType the trigger for the upkeep * @param billingToken the billing token for the upkeep * @param checkData data passed to the contract when checking for upkeep * @param triggerConfig the config for the trigger * @param offchainConfig arbitrary offchain config for the upkeep */ function registerUpkeep( address target, uint32 gasLimit, address admin, Trigger triggerType, IERC20 billingToken, bytes calldata checkData, bytes memory triggerConfig, bytes memory offchainConfig ) public returns (uint256 id) { if (msg.sender != owner() && !s_registrars.contains(msg.sender)) revert OnlyCallableByOwnerOrRegistrar(); if (!target.isContract()) revert NotAContract(); id = _createID(triggerType); IAutomationForwarder forwarder = IAutomationForwarder( address(new AutomationForwarder(target, address(this), i_automationForwarderLogic)) ); _createUpkeep( id, Upkeep({ overridesEnabled: false, performGas: gasLimit, balance: 0, maxValidBlocknumber: UINT32_MAX, lastPerformedBlockNumber: 0, amountSpent: 0, paused: false, forwarder: forwarder, billingToken: billingToken }), admin, checkData, triggerConfig, offchainConfig ); s_storage.nonce++; emit UpkeepRegistered(id, gasLimit, admin); emit UpkeepCheckDataSet(id, checkData); emit UpkeepTriggerConfigSet(id, triggerConfig); emit UpkeepOffchainConfigSet(id, offchainConfig); return (id); } /** * @notice cancels an upkeep * @param id the upkeepID to cancel * @dev if a user cancels an upkeep, their funds are locked for CANCELLATION_DELAY blocks to * allow any pending performUpkeep txs time to get confirmed */ function cancelUpkeep(uint256 id) external { Upkeep memory upkeep = s_upkeep[id]; bool isOwner = msg.sender == owner(); uint96 minSpend = s_billingConfigs[upkeep.billingToken].minSpend; uint256 height = s_hotVars.chainModule.blockNumber(); if (upkeep.maxValidBlocknumber == 0) revert CannotCancel(); if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); if (!isOwner && msg.sender != s_upkeepAdmin[id]) revert OnlyCallableByOwnerOrAdmin(); if (!isOwner) { height = height + CANCELLATION_DELAY; } s_upkeep[id].maxValidBlocknumber = uint32(height); s_upkeepIDs.remove(id); // charge the cancellation fee if the minSpend is not met uint96 cancellationFee = 0; // cancellationFee is min(max(minSpend - amountSpent, 0), amountLeft) if (upkeep.amountSpent < minSpend) { cancellationFee = minSpend - uint96(upkeep.amountSpent); if (cancellationFee > upkeep.balance) { cancellationFee = upkeep.balance; } } s_upkeep[id].balance = upkeep.balance - cancellationFee; s_reserveAmounts[upkeep.billingToken] = s_reserveAmounts[upkeep.billingToken] - cancellationFee; emit UpkeepCanceled(id, uint64(height)); } /** * @notice migrates upkeeps from one registry to another. * @param ids the upkeepIDs to migrate * @param destination the destination registry address * @dev a transcoder must be set in order to enable migration * @dev migration permissions must be set on *both* sending and receiving registries * @dev only an upkeep admin can migrate their upkeeps * @dev this function is most gas-efficient if upkeepIDs are sorted by billing token * @dev s_billingOverrides and s_upkeepPrivilegeConfig are not migrated in this function */ function migrateUpkeeps(uint256[] calldata ids, address destination) external { if ( s_peerRegistryMigrationPermission[destination] != MigrationPermission.OUTGOING && s_peerRegistryMigrationPermission[destination] != MigrationPermission.BIDIRECTIONAL ) revert MigrationNotPermitted(); if (s_storage.transcoder == ZERO_ADDRESS) revert TranscoderNotSet(); if (ids.length == 0) revert ArrayHasNoEntries(); IERC20 billingToken; uint256 balanceToTransfer; uint256 id; Upkeep memory upkeep; address[] memory admins = new address[](ids.length); Upkeep[] memory upkeeps = new Upkeep[](ids.length); bytes[] memory checkDatas = new bytes[](ids.length); bytes[] memory triggerConfigs = new bytes[](ids.length); bytes[] memory offchainConfigs = new bytes[](ids.length); for (uint256 idx = 0; idx < ids.length; idx++) { id = ids[idx]; upkeep = s_upkeep[id]; if (idx == 0) { billingToken = upkeep.billingToken; balanceToTransfer = upkeep.balance; } // if we encounter a new billing token, send the sum from the last billing token to the destination registry if (upkeep.billingToken != billingToken) { s_reserveAmounts[billingToken] = s_reserveAmounts[billingToken] - balanceToTransfer; billingToken.safeTransfer(destination, balanceToTransfer); billingToken = upkeep.billingToken; balanceToTransfer = upkeep.balance; } else if (idx != 0) { balanceToTransfer += upkeep.balance; } _requireAdminAndNotCancelled(id); upkeep.forwarder.updateRegistry(destination); upkeeps[idx] = upkeep; admins[idx] = s_upkeepAdmin[id]; checkDatas[idx] = s_checkData[id]; triggerConfigs[idx] = s_upkeepTriggerConfig[id]; offchainConfigs[idx] = s_upkeepOffchainConfig[id]; delete s_upkeep[id]; delete s_checkData[id]; delete s_upkeepTriggerConfig[id]; delete s_upkeepOffchainConfig[id]; // nullify existing proposed admin change if an upkeep is being migrated delete s_proposedAdmin[id]; delete s_upkeepAdmin[id]; s_upkeepIDs.remove(id); emit UpkeepMigrated(id, upkeep.balance, destination); } // always transfer the rolling sum in the end s_reserveAmounts[billingToken] = s_reserveAmounts[billingToken] - balanceToTransfer; billingToken.safeTransfer(destination, balanceToTransfer); bytes memory encodedUpkeeps = abi.encode( ids, upkeeps, new address[](ids.length), admins, checkDatas, triggerConfigs, offchainConfigs ); MigratableKeeperRegistryInterfaceV2(destination).receiveUpkeeps( UpkeepTranscoderInterfaceV2(s_storage.transcoder).transcodeUpkeeps( UPKEEP_VERSION_BASE, MigratableKeeperRegistryInterfaceV2(destination).upkeepVersion(), encodedUpkeeps ) ); } /** * @notice received upkeeps migrated from another registry * @param encodedUpkeeps the raw upkeep data to import * @dev this function is never called directly, it is only called by another registry's migrate function * @dev s_billingOverrides and s_upkeepPrivilegeConfig are not handled in this function */ function receiveUpkeeps(bytes calldata encodedUpkeeps) external { if ( s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.INCOMING && s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.BIDIRECTIONAL ) revert MigrationNotPermitted(); ( uint256[] memory ids, Upkeep[] memory upkeeps, address[] memory targets, address[] memory upkeepAdmins, bytes[] memory checkDatas, bytes[] memory triggerConfigs, bytes[] memory offchainConfigs ) = abi.decode(encodedUpkeeps, (uint256[], Upkeep[], address[], address[], bytes[], bytes[], bytes[])); for (uint256 idx = 0; idx < ids.length; idx++) { if (address(upkeeps[idx].forwarder) == ZERO_ADDRESS) { upkeeps[idx].forwarder = IAutomationForwarder( address(new AutomationForwarder(targets[idx], address(this), i_automationForwarderLogic)) ); } _createUpkeep( ids[idx], upkeeps[idx], upkeepAdmins[idx], checkDatas[idx], triggerConfigs[idx], offchainConfigs[idx] ); emit UpkeepReceived(ids[idx], upkeeps[idx].balance, msg.sender); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {AutomationRegistryBase2_3} from "./AutomationRegistryBase2_3.sol"; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; import {AutomationRegistryLogicC2_3} from "./AutomationRegistryLogicC2_3.sol"; import {Chainable} from "../Chainable.sol"; import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; contract AutomationRegistryLogicB2_3 is AutomationRegistryBase2_3, Chainable { using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; using SafeERC20 for IERC20; /** * @param logicC the address of the third logic contract */ constructor( AutomationRegistryLogicC2_3 logicC ) AutomationRegistryBase2_3( logicC.getLinkAddress(), logicC.getLinkUSDFeedAddress(), logicC.getNativeUSDFeedAddress(), logicC.getFastGasFeedAddress(), logicC.getAutomationForwarderLogic(), logicC.getAllowedReadOnlyAddress(), logicC.getPayoutMode(), logicC.getWrappedNativeTokenAddress() ) Chainable(address(logicC)) {} // ================================================================ // | PIPELINE FUNCTIONS | // ================================================================ /** * @notice called by the automation DON to check if work is needed * @param id the upkeep ID to check for work needed * @param triggerData extra contextual data about the trigger (not used in all code paths) * @dev this one of the core functions called in the hot path * @dev there is a 2nd checkUpkeep function (below) that is being maintained for backwards compatibility * @dev there is an incongruency on what gets returned during failure modes * ex sometimes we include price data, sometimes we omit it depending on the failure */ function checkUpkeep( uint256 id, bytes memory triggerData ) public returns ( bool upkeepNeeded, bytes memory performData, UpkeepFailureReason upkeepFailureReason, uint256 gasUsed, uint256 gasLimit, uint256 fastGasWei, uint256 linkUSD ) { _preventExecution(); Trigger triggerType = _getTriggerType(id); HotVars memory hotVars = s_hotVars; Upkeep memory upkeep = s_upkeep[id]; { uint256 nativeUSD; uint96 maxPayment; if (hotVars.paused) return (false, bytes(""), UpkeepFailureReason.REGISTRY_PAUSED, 0, upkeep.performGas, 0, 0); if (upkeep.maxValidBlocknumber != UINT32_MAX) return (false, bytes(""), UpkeepFailureReason.UPKEEP_CANCELLED, 0, upkeep.performGas, 0, 0); if (upkeep.paused) return (false, bytes(""), UpkeepFailureReason.UPKEEP_PAUSED, 0, upkeep.performGas, 0, 0); (fastGasWei, linkUSD, nativeUSD) = _getFeedData(hotVars); maxPayment = _getMaxPayment( id, hotVars, triggerType, upkeep.performGas, fastGasWei, linkUSD, nativeUSD, upkeep.billingToken ); if (upkeep.balance < maxPayment) { return (false, bytes(""), UpkeepFailureReason.INSUFFICIENT_BALANCE, 0, upkeep.performGas, 0, 0); } } bytes memory callData = _checkPayload(id, triggerType, triggerData); gasUsed = gasleft(); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory result) = upkeep.forwarder.getTarget().call{gas: s_storage.checkGasLimit}(callData); gasUsed = gasUsed - gasleft(); if (!success) { // User's target check reverted. We capture the revert data here and pass it within performData if (result.length > s_storage.maxRevertDataSize) { return ( false, bytes(""), UpkeepFailureReason.REVERT_DATA_EXCEEDS_LIMIT, gasUsed, upkeep.performGas, fastGasWei, linkUSD ); } return ( upkeepNeeded, result, UpkeepFailureReason.TARGET_CHECK_REVERTED, gasUsed, upkeep.performGas, fastGasWei, linkUSD ); } (upkeepNeeded, performData) = abi.decode(result, (bool, bytes)); if (!upkeepNeeded) return (false, bytes(""), UpkeepFailureReason.UPKEEP_NOT_NEEDED, gasUsed, upkeep.performGas, fastGasWei, linkUSD); if (performData.length > s_storage.maxPerformDataSize) return ( false, bytes(""), UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, gasUsed, upkeep.performGas, fastGasWei, linkUSD ); return (upkeepNeeded, performData, upkeepFailureReason, gasUsed, upkeep.performGas, fastGasWei, linkUSD); } /** * @notice see other checkUpkeep function for description * @dev this function may be deprecated in a future version of chainlink automation */ function checkUpkeep( uint256 id ) external returns ( bool upkeepNeeded, bytes memory performData, UpkeepFailureReason upkeepFailureReason, uint256 gasUsed, uint256 gasLimit, uint256 fastGasWei, uint256 linkUSD ) { return checkUpkeep(id, bytes("")); } /** * @dev checkCallback is used specifically for automation data streams lookups (see StreamsLookupCompatibleInterface.sol) * @param id the upkeepID to execute a callback for * @param values the values returned from the data streams lookup * @param extraData the user-provided extra context data */ function checkCallback( uint256 id, bytes[] memory values, bytes calldata extraData ) external returns (bool upkeepNeeded, bytes memory performData, UpkeepFailureReason upkeepFailureReason, uint256 gasUsed) { bytes memory payload = abi.encodeWithSelector(CHECK_CALLBACK_SELECTOR, values, extraData); return executeCallback(id, payload); } /** * @notice this is a generic callback executor that forwards a call to a user's contract with the configured * gas limit * @param id the upkeepID to execute a callback for * @param payload the data (including function selector) to call on the upkeep target contract */ function executeCallback( uint256 id, bytes memory payload ) public returns (bool upkeepNeeded, bytes memory performData, UpkeepFailureReason upkeepFailureReason, uint256 gasUsed) { _preventExecution(); Upkeep memory upkeep = s_upkeep[id]; gasUsed = gasleft(); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory result) = upkeep.forwarder.getTarget().call{gas: s_storage.checkGasLimit}(payload); gasUsed = gasUsed - gasleft(); if (!success) { return (false, bytes(""), UpkeepFailureReason.CALLBACK_REVERTED, gasUsed); } (upkeepNeeded, performData) = abi.decode(result, (bool, bytes)); if (!upkeepNeeded) { return (false, bytes(""), UpkeepFailureReason.UPKEEP_NOT_NEEDED, gasUsed); } if (performData.length > s_storage.maxPerformDataSize) { return (false, bytes(""), UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, gasUsed); } return (upkeepNeeded, performData, upkeepFailureReason, gasUsed); } /** * @notice simulates the upkeep with the perform data returned from checkUpkeep * @param id identifier of the upkeep to execute the data with. * @param performData calldata parameter to be passed to the target upkeep. * @return success whether the call reverted or not * @return gasUsed the amount of gas the target contract consumed */ function simulatePerformUpkeep( uint256 id, bytes calldata performData ) external returns (bool success, uint256 gasUsed) { _preventExecution(); if (s_hotVars.paused) revert RegistryPaused(); Upkeep memory upkeep = s_upkeep[id]; (success, gasUsed) = _performUpkeep(upkeep.forwarder, upkeep.performGas, performData); return (success, gasUsed); } // ================================================================ // | UPKEEP MANAGEMENT | // ================================================================ /** * @notice adds fund to an upkeep * @param id the upkeepID * @param amount the amount of funds to add, in the upkeep's billing token */ function addFunds(uint256 id, uint96 amount) external payable { Upkeep memory upkeep = s_upkeep[id]; if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); if (msg.value != 0) { if (upkeep.billingToken != IERC20(i_wrappedNativeToken)) { revert InvalidToken(); } amount = SafeCast.toUint96(msg.value); } s_upkeep[id].balance = upkeep.balance + amount; s_reserveAmounts[upkeep.billingToken] = s_reserveAmounts[upkeep.billingToken] + amount; if (msg.value == 0) { // ERC20 payment upkeep.billingToken.safeTransferFrom(msg.sender, address(this), amount); } else { // native payment i_wrappedNativeToken.deposit{value: amount}(); } emit FundsAdded(id, msg.sender, amount); } /** * @notice overrides the billing config for an upkeep * @param id the upkeepID * @param billingOverrides the override-able billing config */ function setBillingOverrides(uint256 id, BillingOverrides calldata billingOverrides) external { _onlyPrivilegeManagerAllowed(); if (s_upkeep[id].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); s_upkeep[id].overridesEnabled = true; s_billingOverrides[id] = billingOverrides; emit BillingConfigOverridden(id, billingOverrides); } /** * @notice remove the overridden billing config for an upkeep * @param id the upkeepID */ function removeBillingOverrides(uint256 id) external { _onlyPrivilegeManagerAllowed(); s_upkeep[id].overridesEnabled = false; delete s_billingOverrides[id]; emit BillingConfigOverrideRemoved(id); } /** * @notice transfers the address of an admin for an upkeep */ function transferUpkeepAdmin(uint256 id, address proposed) external { _requireAdminAndNotCancelled(id); if (proposed == msg.sender) revert ValueNotChanged(); if (s_proposedAdmin[id] != proposed) { s_proposedAdmin[id] = proposed; emit UpkeepAdminTransferRequested(id, msg.sender, proposed); } } /** * @notice accepts the transfer of an upkeep admin */ function acceptUpkeepAdmin(uint256 id) external { Upkeep memory upkeep = s_upkeep[id]; if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); if (s_proposedAdmin[id] != msg.sender) revert OnlyCallableByProposedAdmin(); address past = s_upkeepAdmin[id]; s_upkeepAdmin[id] = msg.sender; s_proposedAdmin[id] = ZERO_ADDRESS; emit UpkeepAdminTransferred(id, past, msg.sender); } /** * @notice pauses an upkeep - an upkeep will be neither checked nor performed while paused */ function pauseUpkeep(uint256 id) external { _requireAdminAndNotCancelled(id); Upkeep memory upkeep = s_upkeep[id]; if (upkeep.paused) revert OnlyUnpausedUpkeep(); s_upkeep[id].paused = true; s_upkeepIDs.remove(id); emit UpkeepPaused(id); } /** * @notice unpauses an upkeep */ function unpauseUpkeep(uint256 id) external { _requireAdminAndNotCancelled(id); Upkeep memory upkeep = s_upkeep[id]; if (!upkeep.paused) revert OnlyPausedUpkeep(); s_upkeep[id].paused = false; s_upkeepIDs.add(id); emit UpkeepUnpaused(id); } /** * @notice updates the checkData for an upkeep */ function setUpkeepCheckData(uint256 id, bytes calldata newCheckData) external { _requireAdminAndNotCancelled(id); if (newCheckData.length > s_storage.maxCheckDataSize) revert CheckDataExceedsLimit(); s_checkData[id] = newCheckData; emit UpkeepCheckDataSet(id, newCheckData); } /** * @notice updates the gas limit for an upkeep */ function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external { if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); _requireAdminAndNotCancelled(id); s_upkeep[id].performGas = gasLimit; emit UpkeepGasLimitSet(id, gasLimit); } /** * @notice updates the offchain config for an upkeep */ function setUpkeepOffchainConfig(uint256 id, bytes calldata config) external { _requireAdminAndNotCancelled(id); s_upkeepOffchainConfig[id] = config; emit UpkeepOffchainConfigSet(id, config); } /** * @notice sets the upkeep trigger config * @param id the upkeepID to change the trigger for * @param triggerConfig the new trigger config */ function setUpkeepTriggerConfig(uint256 id, bytes calldata triggerConfig) external { _requireAdminAndNotCancelled(id); s_upkeepTriggerConfig[id] = triggerConfig; emit UpkeepTriggerConfigSet(id, triggerConfig); } /** * @notice withdraws an upkeep's funds from an upkeep * @dev note that an upkeep must be cancelled first!! */ function withdrawFunds(uint256 id, address to) external nonReentrant { if (to == ZERO_ADDRESS) revert InvalidRecipient(); Upkeep memory upkeep = s_upkeep[id]; if (s_upkeepAdmin[id] != msg.sender) revert OnlyCallableByAdmin(); if (upkeep.maxValidBlocknumber > s_hotVars.chainModule.blockNumber()) revert UpkeepNotCanceled(); uint96 amountToWithdraw = s_upkeep[id].balance; s_reserveAmounts[upkeep.billingToken] = s_reserveAmounts[upkeep.billingToken] - amountToWithdraw; s_upkeep[id].balance = 0; upkeep.billingToken.safeTransfer(to, amountToWithdraw); emit FundsWithdrawn(id, amountToWithdraw, to); } // ================================================================ // | FINANCE ACTIONS | // ================================================================ /** * @notice withdraws excess LINK from the liquidity pool * @param to the address to send the fees to * @param amount the amount to withdraw */ function withdrawLink(address to, uint256 amount) external { _onlyFinanceAdminAllowed(); if (to == ZERO_ADDRESS) revert InvalidRecipient(); int256 available = _linkAvailableForPayment(); if (available < 0) { revert InsufficientBalance(0, amount); } else if (amount > uint256(available)) { revert InsufficientBalance(uint256(available), amount); } bool transferStatus = i_link.transfer(to, amount); if (!transferStatus) { revert TransferFailed(); } emit FeesWithdrawn(address(i_link), to, amount); } /** * @notice withdraws non-LINK fees earned by the contract * @param asset the asset to withdraw * @param to the address to send the fees to * @param amount the amount to withdraw * @dev in ON_CHAIN mode, we prevent withdrawing non-LINK fees unless there is sufficient LINK liquidity * to cover all outstanding debts on the registry */ function withdrawERC20Fees(IERC20 asset, address to, uint256 amount) external { _onlyFinanceAdminAllowed(); if (to == ZERO_ADDRESS) revert InvalidRecipient(); if (address(asset) == address(i_link)) revert InvalidToken(); if (_linkAvailableForPayment() < 0 && s_payoutMode == PayoutMode.ON_CHAIN) revert InsufficientLinkLiquidity(); uint256 available = asset.balanceOf(address(this)) - s_reserveAmounts[asset]; if (amount > available) revert InsufficientBalance(available, amount); asset.safeTransfer(to, amount); emit FeesWithdrawn(address(asset), to, amount); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {AutomationRegistryBase2_3} from "./AutomationRegistryBase2_3.sol"; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; import {IChainModule} from "../interfaces/IChainModule.sol"; import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IAutomationV21PlusCommon} from "../interfaces/IAutomationV21PlusCommon.sol"; contract AutomationRegistryLogicC2_3 is AutomationRegistryBase2_3 { using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; /** * @dev see AutomationRegistry master contract for constructor description */ constructor( address link, address linkUSDFeed, address nativeUSDFeed, address fastGasFeed, address automationForwarderLogic, address allowedReadOnlyAddress, PayoutMode payoutMode, address wrappedNativeTokenAddress ) AutomationRegistryBase2_3( link, linkUSDFeed, nativeUSDFeed, fastGasFeed, automationForwarderLogic, allowedReadOnlyAddress, payoutMode, wrappedNativeTokenAddress ) {} // ================================================================ // | NODE ACTIONS | // ================================================================ /** * @notice transfers the address of payee for a transmitter */ function transferPayeeship(address transmitter, address proposed) external { if (s_transmitterPayees[transmitter] != msg.sender) revert OnlyCallableByPayee(); if (proposed == msg.sender) revert ValueNotChanged(); if (s_proposedPayee[transmitter] != proposed) { s_proposedPayee[transmitter] = proposed; emit PayeeshipTransferRequested(transmitter, msg.sender, proposed); } } /** * @notice accepts the transfer of the payee */ function acceptPayeeship(address transmitter) external { if (s_proposedPayee[transmitter] != msg.sender) revert OnlyCallableByProposedPayee(); address past = s_transmitterPayees[transmitter]; s_transmitterPayees[transmitter] = msg.sender; s_proposedPayee[transmitter] = ZERO_ADDRESS; emit PayeeshipTransferred(transmitter, past, msg.sender); } /** * @notice this is for NOPs to withdraw LINK received as payment for work performed */ function withdrawPayment(address from, address to) external { if (to == ZERO_ADDRESS) revert InvalidRecipient(); if (s_payoutMode == PayoutMode.OFF_CHAIN) revert MustSettleOffchain(); if (s_transmitterPayees[from] != msg.sender) revert OnlyCallableByPayee(); uint96 balance = _updateTransmitterBalanceFromPool(from, s_hotVars.totalPremium, uint96(s_transmittersList.length)); s_transmitters[from].balance = 0; s_reserveAmounts[IERC20(address(i_link))] = s_reserveAmounts[IERC20(address(i_link))] - balance; bool transferStatus = i_link.transfer(to, balance); if (!transferStatus) { revert TransferFailed(); } emit PaymentWithdrawn(from, balance, to, msg.sender); } // ================================================================ // | OWNER / MANAGER ACTIONS | // ================================================================ /** * @notice sets the privilege config for an upkeep */ function setUpkeepPrivilegeConfig(uint256 upkeepId, bytes calldata newPrivilegeConfig) external { _onlyPrivilegeManagerAllowed(); s_upkeepPrivilegeConfig[upkeepId] = newPrivilegeConfig; emit UpkeepPrivilegeConfigSet(upkeepId, newPrivilegeConfig); } /** * @notice this is used by the owner to set the initial payees for newly added transmitters. The owner is not allowed to change payees for existing transmitters. * @dev the IGNORE_ADDRESS is a "helper" that makes it easier to construct a list of payees when you only care about setting the payee for a small number of transmitters. */ function setPayees(address[] calldata payees) external onlyOwner { if (s_transmittersList.length != payees.length) revert ParameterLengthError(); for (uint256 i = 0; i < s_transmittersList.length; i++) { address transmitter = s_transmittersList[i]; address oldPayee = s_transmitterPayees[transmitter]; address newPayee = payees[i]; if ( (newPayee == ZERO_ADDRESS) || (oldPayee != ZERO_ADDRESS && oldPayee != newPayee && newPayee != IGNORE_ADDRESS) ) { revert InvalidPayee(); } if (newPayee != IGNORE_ADDRESS) { s_transmitterPayees[transmitter] = newPayee; } } emit PayeesUpdated(s_transmittersList, payees); } /** * @notice sets the migration permission for a peer registry * @dev this must be done before upkeeps can be migrated to/from another registry */ function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external onlyOwner { s_peerRegistryMigrationPermission[peer] = permission; } /** * @notice pauses the entire registry */ function pause() external onlyOwner { s_hotVars.paused = true; emit Paused(msg.sender); } /** * @notice unpauses the entire registry */ function unpause() external onlyOwner { s_hotVars.paused = false; emit Unpaused(msg.sender); } /** * @notice sets a generic bytes field used to indicate the privilege that this admin address had * @param admin the address to set privilege for * @param newPrivilegeConfig the privileges that this admin has */ function setAdminPrivilegeConfig(address admin, bytes calldata newPrivilegeConfig) external { _onlyPrivilegeManagerAllowed(); s_adminPrivilegeConfig[admin] = newPrivilegeConfig; emit AdminPrivilegeConfigSet(admin, newPrivilegeConfig); } /** * @notice settles NOPs' LINK payment offchain */ function settleNOPsOffchain() external { _onlyFinanceAdminAllowed(); if (s_payoutMode == PayoutMode.ON_CHAIN) revert MustSettleOnchain(); uint96 totalPremium = s_hotVars.totalPremium; uint256 activeTransmittersLength = s_transmittersList.length; uint256 deactivatedTransmittersLength = s_deactivatedTransmitters.length(); uint256 length = activeTransmittersLength + deactivatedTransmittersLength; uint256[] memory payments = new uint256[](length); address[] memory payees = new address[](length); for (uint256 i = 0; i < activeTransmittersLength; i++) { address transmitterAddr = s_transmittersList[i]; uint96 balance = _updateTransmitterBalanceFromPool( transmitterAddr, totalPremium, uint96(activeTransmittersLength) ); payments[i] = balance; payees[i] = s_transmitterPayees[transmitterAddr]; s_transmitters[transmitterAddr].balance = 0; } for (uint256 i = 0; i < deactivatedTransmittersLength; i++) { address deactivatedAddr = s_deactivatedTransmitters.at(i); Transmitter memory transmitter = s_transmitters[deactivatedAddr]; payees[i + activeTransmittersLength] = s_transmitterPayees[deactivatedAddr]; payments[i + activeTransmittersLength] = transmitter.balance; s_transmitters[deactivatedAddr].balance = 0; } // reserve amount of LINK is reset to 0 since no user deposits of LINK are expected in offchain mode s_reserveAmounts[IERC20(address(i_link))] = 0; for (uint256 idx = s_deactivatedTransmitters.length(); idx > 0; idx--) { s_deactivatedTransmitters.remove(s_deactivatedTransmitters.at(idx - 1)); } emit NOPsSettledOffchain(payees, payments); } /** * @notice disables offchain payment for NOPs */ function disableOffchainPayments() external onlyOwner { s_payoutMode = PayoutMode.ON_CHAIN; } // ================================================================ // | GETTERS | // ================================================================ function getConditionalGasOverhead() external pure returns (uint256) { return REGISTRY_CONDITIONAL_OVERHEAD; } function getLogGasOverhead() external pure returns (uint256) { return REGISTRY_LOG_OVERHEAD; } function getPerPerformByteGasOverhead() external pure returns (uint256) { return REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD; } function getPerSignerGasOverhead() external pure returns (uint256) { return REGISTRY_PER_SIGNER_GAS_OVERHEAD; } function getTransmitCalldataFixedBytesOverhead() external pure returns (uint256) { return TRANSMIT_CALLDATA_FIXED_BYTES_OVERHEAD; } function getTransmitCalldataPerSignerBytesOverhead() external pure returns (uint256) { return TRANSMIT_CALLDATA_PER_SIGNER_BYTES_OVERHEAD; } function getCancellationDelay() external pure returns (uint256) { return CANCELLATION_DELAY; } function getLinkAddress() external view returns (address) { return address(i_link); } function getLinkUSDFeedAddress() external view returns (address) { return address(i_linkUSDFeed); } function getNativeUSDFeedAddress() external view returns (address) { return address(i_nativeUSDFeed); } function getFastGasFeedAddress() external view returns (address) { return address(i_fastGasFeed); } function getAutomationForwarderLogic() external view returns (address) { return i_automationForwarderLogic; } function getAllowedReadOnlyAddress() external view returns (address) { return i_allowedReadOnlyAddress; } function getWrappedNativeTokenAddress() external view returns (address) { return address(i_wrappedNativeToken); } function getBillingToken(uint256 upkeepID) external view returns (IERC20) { return s_upkeep[upkeepID].billingToken; } function getBillingTokens() external view returns (IERC20[] memory) { return s_billingTokens; } function supportsBillingToken(IERC20 token) external view returns (bool) { return address(s_billingConfigs[token].priceFeed) != address(0); } function getBillingTokenConfig(IERC20 token) external view returns (BillingConfig memory) { return s_billingConfigs[token]; } function getBillingOverridesEnabled(uint256 upkeepID) external view returns (bool) { return s_upkeep[upkeepID].overridesEnabled; } function getPayoutMode() external view returns (PayoutMode) { return s_payoutMode; } function upkeepVersion() public pure returns (uint8) { return UPKEEP_VERSION_BASE; } /** * @notice gets the number of upkeeps on the registry */ function getNumUpkeeps() external view returns (uint256) { return s_upkeepIDs.length(); } /** * @notice read all of the details about an upkeep * @dev this function may be deprecated in a future version of automation in favor of individual * getters for each field */ function getUpkeep(uint256 id) external view returns (IAutomationV21PlusCommon.UpkeepInfoLegacy memory upkeepInfo) { Upkeep memory reg = s_upkeep[id]; address target = address(reg.forwarder) == address(0) ? address(0) : reg.forwarder.getTarget(); upkeepInfo = IAutomationV21PlusCommon.UpkeepInfoLegacy({ target: target, performGas: reg.performGas, checkData: s_checkData[id], balance: reg.balance, admin: s_upkeepAdmin[id], maxValidBlocknumber: reg.maxValidBlocknumber, lastPerformedBlockNumber: reg.lastPerformedBlockNumber, amountSpent: uint96(reg.amountSpent), // force casting to uint96 for backwards compatibility. Not an issue if it overflows. paused: reg.paused, offchainConfig: s_upkeepOffchainConfig[id] }); return upkeepInfo; } /** * @notice retrieve active upkeep IDs. Active upkeep is defined as an upkeep which is not paused and not canceled. * @param startIndex starting index in list * @param maxCount max count to retrieve (0 = unlimited) * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one * should consider keeping the blockheight constant to ensure a holistic picture of the contract state */ function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory) { uint256 numUpkeeps = s_upkeepIDs.length(); if (startIndex >= numUpkeeps) revert IndexOutOfRange(); uint256 endIndex = startIndex + maxCount; endIndex = endIndex > numUpkeeps || maxCount == 0 ? numUpkeeps : endIndex; uint256[] memory ids = new uint256[](endIndex - startIndex); for (uint256 idx = 0; idx < ids.length; idx++) { ids[idx] = s_upkeepIDs.at(idx + startIndex); } return ids; } /** * @notice returns the upkeep's trigger type */ function getTriggerType(uint256 upkeepId) external pure returns (Trigger) { return _getTriggerType(upkeepId); } /** * @notice returns the trigger config for an upkeeep */ function getUpkeepTriggerConfig(uint256 upkeepId) public view returns (bytes memory) { return s_upkeepTriggerConfig[upkeepId]; } /** * @notice read the current info about any transmitter address */ function getTransmitterInfo( address query ) external view returns (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee) { Transmitter memory transmitter = s_transmitters[query]; uint96 pooledShare = 0; if (transmitter.active) { uint96 totalDifference = s_hotVars.totalPremium - transmitter.lastCollected; pooledShare = totalDifference / uint96(s_transmittersList.length); } return ( transmitter.active, transmitter.index, (transmitter.balance + pooledShare), transmitter.lastCollected, s_transmitterPayees[query] ); } /** * @notice read the current info about any signer address */ function getSignerInfo(address query) external view returns (bool active, uint8 index) { Signer memory signer = s_signers[query]; return (signer.active, signer.index); } /** * @notice read the current on-chain config of the registry * @dev this function will change between versions, it should never be used where * backwards compatibility matters! */ function getConfig() external view returns (OnchainConfig memory) { return OnchainConfig({ checkGasLimit: s_storage.checkGasLimit, stalenessSeconds: s_hotVars.stalenessSeconds, gasCeilingMultiplier: s_hotVars.gasCeilingMultiplier, maxPerformGas: s_storage.maxPerformGas, maxCheckDataSize: s_storage.maxCheckDataSize, maxPerformDataSize: s_storage.maxPerformDataSize, maxRevertDataSize: s_storage.maxRevertDataSize, fallbackGasPrice: s_fallbackGasPrice, fallbackLinkPrice: s_fallbackLinkPrice, fallbackNativePrice: s_fallbackNativePrice, transcoder: s_storage.transcoder, registrars: s_registrars.values(), upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager, chainModule: s_hotVars.chainModule, reorgProtectionEnabled: s_hotVars.reorgProtectionEnabled, financeAdmin: s_storage.financeAdmin }); } /** * @notice read the current state of the registry * @dev this function is deprecated */ function getState() external view returns ( IAutomationV21PlusCommon.StateLegacy memory state, IAutomationV21PlusCommon.OnchainConfigLegacy memory config, address[] memory signers, address[] memory transmitters, uint8 f ) { state = IAutomationV21PlusCommon.StateLegacy({ nonce: s_storage.nonce, ownerLinkBalance: 0, // deprecated expectedLinkBalance: 0, // deprecated totalPremium: s_hotVars.totalPremium, numUpkeeps: s_upkeepIDs.length(), configCount: s_storage.configCount, latestConfigBlockNumber: s_storage.latestConfigBlockNumber, latestConfigDigest: s_latestConfigDigest, latestEpoch: s_hotVars.latestEpoch, paused: s_hotVars.paused }); config = IAutomationV21PlusCommon.OnchainConfigLegacy({ paymentPremiumPPB: 0, // deprecated flatFeeMicroLink: 0, // deprecated checkGasLimit: s_storage.checkGasLimit, stalenessSeconds: s_hotVars.stalenessSeconds, gasCeilingMultiplier: s_hotVars.gasCeilingMultiplier, minUpkeepSpend: 0, // deprecated maxPerformGas: s_storage.maxPerformGas, maxCheckDataSize: s_storage.maxCheckDataSize, maxPerformDataSize: s_storage.maxPerformDataSize, maxRevertDataSize: s_storage.maxRevertDataSize, fallbackGasPrice: s_fallbackGasPrice, fallbackLinkPrice: s_fallbackLinkPrice, transcoder: s_storage.transcoder, registrars: s_registrars.values(), upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager }); return (state, config, s_signersList, s_transmittersList, s_hotVars.f); } /** * @notice read the Storage data * @dev this function signature will change with each version of automation * this should not be treated as a stable function */ function getStorage() external view returns (Storage memory) { return s_storage; } /** * @notice read the HotVars data * @dev this function signature will change with each version of automation * this should not be treated as a stable function */ function getHotVars() external view returns (HotVars memory) { return s_hotVars; } /** * @notice get the chain module */ function getChainModule() external view returns (IChainModule chainModule) { return s_hotVars.chainModule; } /** * @notice if this registry has reorg protection enabled */ function getReorgProtectionEnabled() external view returns (bool reorgProtectionEnabled) { return s_hotVars.reorgProtectionEnabled; } /** * @notice calculates the minimum balance required for an upkeep to remain eligible * @param id the upkeep id to calculate minimum balance for */ function getBalance(uint256 id) external view returns (uint96 balance) { return s_upkeep[id].balance; } /** * @notice calculates the minimum balance required for an upkeep to remain eligible * @param id the upkeep id to calculate minimum balance for */ function getMinBalance(uint256 id) external view returns (uint96) { return getMinBalanceForUpkeep(id); } /** * @notice calculates the minimum balance required for an upkeep to remain eligible * @param id the upkeep id to calculate minimum balance for * @dev this will be deprecated in a future version in favor of getMinBalance */ function getMinBalanceForUpkeep(uint256 id) public view returns (uint96 minBalance) { Upkeep memory upkeep = s_upkeep[id]; return getMaxPaymentForGas(id, _getTriggerType(id), upkeep.performGas, upkeep.billingToken); } /** * @notice calculates the maximum payment for a given gas limit * @param gasLimit the gas to calculate payment for */ function getMaxPaymentForGas( uint256 id, Trigger triggerType, uint32 gasLimit, IERC20 billingToken ) public view returns (uint96 maxPayment) { HotVars memory hotVars = s_hotVars; (uint256 fastGasWei, uint256 linkUSD, uint256 nativeUSD) = _getFeedData(hotVars); return _getMaxPayment(id, hotVars, triggerType, gasLimit, fastGasWei, linkUSD, nativeUSD, billingToken); } /** * @notice retrieves the migration permission for a peer registry */ function getPeerRegistryMigrationPermission(address peer) external view returns (MigrationPermission) { return s_peerRegistryMigrationPermission[peer]; } /** * @notice returns the upkeep privilege config */ function getUpkeepPrivilegeConfig(uint256 upkeepId) external view returns (bytes memory) { return s_upkeepPrivilegeConfig[upkeepId]; } /** * @notice returns the admin's privilege config */ function getAdminPrivilegeConfig(address admin) external view returns (bytes memory) { return s_adminPrivilegeConfig[admin]; } /** * @notice returns the upkeep's forwarder contract */ function getForwarder(uint256 upkeepID) external view returns (IAutomationForwarder) { return s_upkeep[upkeepID].forwarder; } /** * @notice returns if the dedupKey exists or not */ function hasDedupKey(bytes32 dedupKey) external view returns (bool) { return s_dedupKeys[dedupKey]; } /** * @notice returns the fallback native price */ function getFallbackNativePrice() external view returns (uint256) { return s_fallbackNativePrice; } /** * @notice returns the amount of a particular token that is reserved as * user deposits / NOP payments */ function getReserveAmount(IERC20 billingToken) external view returns (uint256) { return s_reserveAmounts[billingToken]; } /** * @notice returns the amount of a particular token that is withdraw-able by finance admin */ function getAvailableERC20ForPayment(IERC20 billingToken) external view returns (uint256) { return billingToken.balanceOf(address(this)) - s_reserveAmounts[IERC20(address(billingToken))]; } /** * @notice returns the size of the LINK liquidity pool */ function linkAvailableForPayment() public view returns (int256) { return _linkAvailableForPayment(); } /** * @notice returns the BillingOverrides config for a given upkeep */ function getBillingOverrides(uint256 upkeepID) external view returns (BillingOverrides memory) { return s_billingOverrides[upkeepID]; } /** * @notice returns the BillingConfig for a given billing token, this includes decimals and price feed etc */ function getBillingConfig(IERC20 billingToken) external view returns (BillingConfig memory) { return s_billingConfigs[billingToken]; } /** * @notice returns all active transmitters with their associated payees */ function getTransmittersWithPayees() external view returns (TransmitterPayeeInfo[] memory) { uint256 transmitterCount = s_transmittersList.length; TransmitterPayeeInfo[] memory transmitters = new TransmitterPayeeInfo[](transmitterCount); for (uint256 i = 0; i < transmitterCount; i++) { address transmitterAddress = s_transmittersList[i]; address payeeAddress = s_transmitterPayees[transmitterAddress]; transmitters[i] = TransmitterPayeeInfo(transmitterAddress, payeeAddress); } return transmitters; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {AutomationRegistryBase2_3} from "./AutomationRegistryBase2_3.sol"; /** * @notice this file exposes structs that are otherwise internal to the automation registry * doing this allows those structs to be encoded and decoded with type safety in offchain code * and tests because generated wrappers are made available */ contract AutomationUtils2_3 { /** * @dev this uses the v2.3 Report, which uses linkUSD instead of linkNative (as in v2.2 and prior). This should be used only in typescript tests. */ function _report(AutomationRegistryBase2_3.Report memory) external {} // 0xe65d6546 }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.19; import {UpkeepTranscoderInterfaceV2} from "../interfaces/UpkeepTranscoderInterfaceV2.sol"; import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; enum RegistryVersion { V12, V13, V20, V21, V23 } /** * @notice UpkeepTranscoder is a contract that allows converting upkeep data from previous registry versions to newer versions * @dev it currently only supports 2.3 -> 2.3 migrations */ contract UpkeepTranscoder5_0 is UpkeepTranscoderInterfaceV2, TypeAndVersionInterface { error InvalidTranscoding(); string public constant override typeAndVersion = "UpkeepTranscoder 5.0.0"; /** * @notice transcodeUpkeeps transforms upkeep data from the format expected by * one registry to the format expected by another. It future-proofs migrations * by allowing automation team to customize migration paths and set sensible defaults * when new fields are added * @param fromVersion version the upkeep is migrating from * @param toVersion version the upkeep is migrating to * @param encodedUpkeeps encoded upkeep data * @dev this transcoder should ONLY be use for V23->V23 migrations for now */ function transcodeUpkeeps( uint8 fromVersion, uint8 toVersion, bytes calldata encodedUpkeeps ) external view override returns (bytes memory) { if (toVersion == uint8(RegistryVersion.V23) && fromVersion == uint8(RegistryVersion.V23)) { return encodedUpkeeps; } revert InvalidTranscoding(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; abstract contract TypeAndVersionInterface { function typeAndVersion() external pure virtual returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol"; /// @title The ConfirmedOwner contract /// @notice A contract with helpers for basic contract ownership. contract ConfirmedOwner is ConfirmedOwnerWithProposal { constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IOwnable} from "../interfaces/IOwnable.sol"; /// @title The ConfirmedOwner contract /// @notice A contract with helpers for basic contract ownership. contract ConfirmedOwnerWithProposal is IOwnable { address private s_owner; address private s_pendingOwner; event OwnershipTransferRequested(address indexed from, address indexed to); event OwnershipTransferred(address indexed from, address indexed to); constructor(address newOwner, address pendingOwner) { // solhint-disable-next-line gas-custom-errors require(newOwner != address(0), "Cannot set owner to zero"); s_owner = newOwner; if (pendingOwner != address(0)) { _transferOwnership(pendingOwner); } } /// @notice Allows an owner to begin transferring ownership to a new address. function transferOwnership(address to) public override onlyOwner { _transferOwnership(to); } /// @notice Allows an ownership transfer to be completed by the recipient. function acceptOwnership() external override { // solhint-disable-next-line gas-custom-errors require(msg.sender == s_pendingOwner, "Must be proposed owner"); address oldOwner = s_owner; s_owner = msg.sender; s_pendingOwner = address(0); emit OwnershipTransferred(oldOwner, msg.sender); } /// @notice Get the current owner function owner() public view override returns (address) { return s_owner; } /// @notice validate, transfer ownership, and emit relevant events function _transferOwnership(address to) private { // solhint-disable-next-line gas-custom-errors require(to != msg.sender, "Cannot transfer to self"); s_pendingOwner = to; emit OwnershipTransferRequested(s_owner, to); } /// @notice validate access function _validateOwnership() internal view { // solhint-disable-next-line gas-custom-errors require(msg.sender == s_owner, "Only callable by owner"); } /// @notice Reverts if called by anyone other than the contract owner. modifier onlyOwner() { _validateOwnership(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable-next-line interface-starts-with-i interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); function getRoundData( uint80 _roundId ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.6; interface IERC677Receiver { function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IOwnable { function owner() external returns (address); function transferOwnership(address recipient) external; function acceptOwnership() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ITypeAndVersion { function typeAndVersion() external pure returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable-next-line interface-starts-with-i interface LinkTokenInterface { function allowance(address owner, address spender) external view returns (uint256 remaining); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external view returns (uint256 balance); function decimals() external view returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external view returns (string memory tokenName); function symbol() external view returns (string memory tokenSymbol); function totalSupply() external view returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ITypeAndVersion} from "../interfaces/ITypeAndVersion.sol"; abstract contract OCR2Abstract is ITypeAndVersion { uint256 internal constant MAX_NUM_ORACLES = 31; uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 uint256 private constant PREFIX = 0x0001 << (256 - 16); // 0x000100..00 /// @notice triggers a new run of the offchain reporting protocol /// @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis /// @param configDigest configDigest of this configuration /// @param configCount ordinal number of this config setting among all config settings over the life of this contract /// @param signers ith element is address ith oracle uses to sign a report /// @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method /// @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly /// @param onchainConfig serialized configuration used by the contract (and possibly oracles) /// @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract event ConfigSet( uint32 previousConfigBlockNumber, bytes32 configDigest, uint64 configCount, address[] signers, address[] transmitters, uint8 f, bytes onchainConfig, uint64 offchainConfigVersion, bytes offchainConfig ); /// @notice sets offchain reporting protocol configuration incl. participating oracles /// @param signers addresses with which oracles sign the reports /// @param transmitters addresses oracles use to transmit the reports /// @param f number of faulty oracles the system can tolerate /// @param onchainConfig serialized configuration used by the contract (and possibly oracles) /// @param offchainConfigVersion version number for offchainEncoding schema /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract function setConfig( address[] memory signers, address[] memory transmitters, uint8 f, bytes memory onchainConfig, uint64 offchainConfigVersion, bytes memory offchainConfig ) external virtual; /// @notice information about current offchain reporting protocol configuration /// @return configCount ordinal number of current config, out of all configs applied to this contract so far /// @return blockNumber block at which this config was set /// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData) function latestConfigDetails() external view virtual returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest); function _configDigestFromConfigData( uint256 chainId, address contractAddress, uint64 configCount, address[] memory signers, address[] memory transmitters, uint8 f, bytes memory onchainConfig, uint64 offchainConfigVersion, bytes memory offchainConfig ) internal pure returns (bytes32) { uint256 h = uint256( keccak256( abi.encode( chainId, contractAddress, configCount, signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig ) ) ); return bytes32((PREFIX & PREFIX_MASK) | (h & ~PREFIX_MASK)); } /// @notice optionally emitted to indicate the latest configDigest and epoch for /// which a report was successfully transmitted. Alternatively, the contract may /// use latestConfigDigestAndEpoch with scanLogs set to false. event Transmitted(bytes32 configDigest, uint32 epoch); /// @notice optionally returns the latest configDigest and epoch for which a /// report was successfully transmitted. Alternatively, the contract may return /// scanLogs set to true and use Transmitted events to provide this information /// to offchain watchers. /// @return scanLogs indicates whether to rely on the configDigest and epoch /// returned or whether to scan logs for the Transmitted event instead. /// @return configDigest /// @return epoch function latestConfigDigestAndEpoch() external view virtual returns (bool scanLogs, bytes32 configDigest, uint32 epoch); /// @notice transmit is called to post a new report to the contract /// @param reportContext [0]: ConfigDigest, [1]: 27 byte padding, 4-byte epoch and 1-byte round, [2]: ExtraHash /// @param report serialized report, which the signatures are signing. /// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries /// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries /// @param rawVs ith element is the the V component of the ith signature function transmit( // NOTE: If these parameters are changed, expectedMsgDataLength and/or // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly bytes32[3] calldata reportContext, bytes calldata report, bytes32[] calldata rs, bytes32[] calldata ss, bytes32 rawVs // signatures ) external virtual; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
{ "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract ABI
API[{"inputs":[{"internalType":"address","name":"LINKAddress","type":"address"},{"internalType":"contract IAutomationRegistryMaster2_3","name":"registry","type":"address"},{"components":[{"internalType":"uint8","name":"triggerType","type":"uint8"},{"internalType":"enum AutomationRegistrar2_3.AutoApproveType","name":"autoApproveType","type":"uint8"},{"internalType":"uint32","name":"autoApproveMaxAllowed","type":"uint32"}],"internalType":"struct AutomationRegistrar2_3.InitialTriggerConfig[]","name":"triggerConfigs","type":"tuple[]"},{"internalType":"contract IERC20Metadata[]","name":"billingTokens","type":"address[]"},{"internalType":"uint256[]","name":"minRegistrationFees","type":"uint256[]"},{"internalType":"contract IWrappedNative","name":"wrappedNativeToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"HashMismatch","type":"error"},{"inputs":[],"name":"InsufficientPayment","type":"error"},{"inputs":[],"name":"InvalidAdminAddress","type":"error"},{"inputs":[],"name":"InvalidBillingToken","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"OnlyAdminOrOwner","type":"error"},{"inputs":[],"name":"OnlyLink","type":"error"},{"inputs":[],"name":"RequestNotFound","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"TransferFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"senderAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"AutoApproveAllowedSenderSet","type":"event"},{"anonymous":false,"inputs":[],"name":"ConfigChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"hash","type":"bytes32"},{"indexed":false,"internalType":"string","name":"displayName","type":"string"},{"indexed":true,"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"RegistrationApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"RegistrationRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"hash","type":"bytes32"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"bytes","name":"encryptedEmail","type":"bytes"},{"indexed":true,"internalType":"address","name":"upkeepContract","type":"address"},{"indexed":false,"internalType":"uint32","name":"gasLimit","type":"uint32"},{"indexed":false,"internalType":"address","name":"adminAddress","type":"address"},{"indexed":false,"internalType":"uint8","name":"triggerType","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"checkData","type":"bytes"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"},{"indexed":false,"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"RegistrationRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"triggerType","type":"uint8"},{"indexed":false,"internalType":"enum AutomationRegistrar2_3.AutoApproveType","name":"autoApproveType","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"autoApproveMaxAllowed","type":"uint32"}],"name":"TriggerConfigSet","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"upkeepContract","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"address","name":"adminAddress","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"uint8","name":"triggerType","type":"uint8"},{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"bytes","name":"encryptedEmail","type":"bytes"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct AutomationRegistrar2_3.RegistrationParams","name":"requestParams","type":"tuple"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"senderAddress","type":"address"}],"name":"getAutoApproveAllowedSender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getMinimumRegistrationAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"getPendingRequest","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRegistry","outputs":[{"internalType":"contract IAutomationRegistryMaster2_3","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"triggerType","type":"uint8"}],"name":"getTriggerRegistrationDetails","outputs":[{"components":[{"internalType":"enum AutomationRegistrar2_3.AutoApproveType","name":"autoApproveType","type":"uint8"},{"internalType":"uint32","name":"autoApproveMaxAllowed","type":"uint32"},{"internalType":"uint32","name":"approvedCount","type":"uint32"}],"internalType":"struct AutomationRegistrar2_3.TriggerRegistrationStorage","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"i_LINK","outputs":[{"internalType":"contract LinkTokenInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"i_WRAPPED_NATIVE_TOKEN","outputs":[{"internalType":"contract IWrappedNative","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"upkeepContract","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"address","name":"adminAddress","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"uint8","name":"triggerType","type":"uint8"},{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"bytes","name":"encryptedEmail","type":"bytes"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct AutomationRegistrar2_3.RegistrationParams","name":"requestParams","type":"tuple"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setAutoApproveAllowedSender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IAutomationRegistryMaster2_3","name":"registry","type":"address"},{"internalType":"contract IERC20Metadata[]","name":"billingTokens","type":"address[]"},{"internalType":"uint256[]","name":"minBalances","type":"uint256[]"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"triggerType","type":"uint8"},{"internalType":"enum AutomationRegistrar2_3.AutoApproveType","name":"autoApproveType","type":"uint8"},{"internalType":"uint32","name":"autoApproveMaxAllowed","type":"uint32"}],"name":"setTriggerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
Deployed Bytecode
0x6080604052600436106101295760003560e01c80637e776f7f116100a5578063a4c0ed3611610074578063befdae4611610059578063befdae46146104a1578063c4d252f5146104d5578063f2fde38b146104f557600080fd5b8063a4c0ed3614610461578063b5ff5b411461048157600080fd5b80637e776f7f146102d257806388b12d55146103285780638da5cb5b146103f3578063a2b1ff941461041e57600080fd5b8063367b9b4f116100fc57806366ab87f9116100e157806366ab87f9146102695780636bf7d75f1461028957806379ba5097146102bd57600080fd5b8063367b9b4f146101fd5780635ab1bd531461021d57600080fd5b8063181f5a771461012e578063212d08841461018d5780632ce3a14a146101ba5780633188a2ce146101db575b600080fd5b34801561013a57600080fd5b506101776040518060400160405280601981526020017f4175746f6d6174696f6e52656769737472617220322e332e300000000000000081525081565b6040516101849190611f57565b60405180910390f35b34801561019957600080fd5b506101ad6101a8366004611f87565b610515565b604051610184919061200c565b6101cd6101c8366004612323565b6105a2565b604051908152602001610184565b3480156101e757600080fd5b506101fb6101f6366004612358565b610710565b005b34801561020957600080fd5b506101fb6102183660046123a2565b610859565b34801561022957600080fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610184565b34801561027557600080fd5b506101fb61028436600461246a565b6108eb565b34801561029557600080fd5b506102447f000000000000000000000000530000000000000000000000000000000000000481565b3480156102c957600080fd5b506101fb610a32565b3480156102de57600080fd5b506103186102ed366004612540565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205460ff1690565b6040519015158152602001610184565b34801561033457600080fd5b506103ba61034336600461255d565b6000908152600560209081526040918290208251606081018452815473ffffffffffffffffffffffffffffffffffffffff808216808452740100000000000000000000000000000000000000009092046bffffffffffffffffffffffff169483018590526001909301549092169301929092529091565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526bffffffffffffffffffffffff909116602083015201610184565b3480156103ff57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16610244565b34801561042a57600080fd5b506101cd610439366004612540565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b34801561046d57600080fd5b506101fb61047c366004612576565b610b34565b34801561048d57600080fd5b506101fb61049c3660046125ff565b610c62565b3480156104ad57600080fd5b506102447f000000000000000000000000231d45b53c905c3d6201318156bdc725c9c3b9b181565b3480156104e157600080fd5b506101fb6104f036600461255d565b610d41565b34801561050157600080fd5b506101fb610510366004612540565b610f08565b60408051606080820183526000808352602080840182905283850182905260ff86811683526006909152908490208451928301909452835492939192839116600281111561056557610565611fa2565b600281111561057657610576611fa2565b8152905463ffffffff610100820481166020840152650100000000009091041660409091015292915050565b60007f000000000000000000000000530000000000000000000000000000000000000473ffffffffffffffffffffffffffffffffffffffff168260a0015173ffffffffffffffffffffffffffffffffffffffff1614801561060257503415155b156106bd5761061034610f1c565b82602001906bffffffffffffffffffffffff1690816bffffffffffffffffffffffff16815250507f000000000000000000000000530000000000000000000000000000000000000473ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561069f57600080fd5b505af11580156106b3573d6000803e3d6000fd5b5050505050610700565b610700333084602001516bffffffffffffffffffffffff168560a0015173ffffffffffffffffffffffffffffffffffffffff16610fbe909392919063ffffffff16565b61070a823361109a565b92915050565b610718611507565b60008160405160200161072b91906126fc565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181528282528051602091820120600081815260058352839020606085018452805473ffffffffffffffffffffffffffffffffffffffff808216808852740100000000000000000000000000000000000000009092046bffffffffffffffffffffffff169487019490945260019091015490921692840192909252909250610807576040517f4b13b31e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260056020526040812090815560010180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905561085361084d846128b8565b8361158a565b50505050565b610861611507565b73ffffffffffffffffffffffffffffffffffffffff821660008181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527f20c6237dac83526a849285a9f79d08a483291bdd3a056a0ef9ae94ecee1ad356910160405180910390a25050565b6108f3611507565b805182511461092e576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851617905560005b8251811015610a035781818151811061098c5761098c6128c4565b6020026020010151600460008584815181106109aa576109aa6128c4565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555080806109fb90612922565b915050610971565b506040517fb9b6902016bd1219d5fa6161243b61e7e9f7f959526dd94ef8fa3e403bf881c390600090a1505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610ab8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000231d45b53c905c3d6201318156bdc725c9c3b9b11614610ba3576040517f018d10be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610bb182840184612323565b90507f000000000000000000000000231d45b53c905c3d6201318156bdc725c9c3b9b173ffffffffffffffffffffffffffffffffffffffff168160a0015173ffffffffffffffffffffffffffffffffffffffff1614610c3c576040517f018d10be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6bffffffffffffffffffffffff84166020820152610c5a818661109a565b505050505050565b610c6a611507565b60ff8316600090815260066020526040902080548391907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836002811115610cb757610cb7611fa2565b021790555060ff83166000908152600660205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff1661010063ffffffff851602179055517f830a6d06a4e2caac67eba04323de22bdb04f032dd8b3d6a0c52b503d9a7036a390610d349085908590859061295a565b60405180910390a1505050565b6000818152600560209081526040918290208251606081018452815473ffffffffffffffffffffffffffffffffffffffff808216808452740100000000000000000000000000000000000000009092046bffffffffffffffffffffffff16948301949094526001909201549092169282019290925290331480610ddb575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610e11576040517f61685c2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16610e5f576040517f4b13b31e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600560209081526040808320928355600190920180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905582519083015191830151610ed99273ffffffffffffffffffffffffffffffffffffffff90911691906bffffffffffffffffffffffff166118f5565b60405182907f3663fb28ebc87645eb972c9dad8521bf665c623f287e79f1c56f1eb374b82a2290600090a25050565b610f10611507565b610f1981611950565b50565b60006bffffffffffffffffffffffff821115610fba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610aaf565b5090565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526108539085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611a45565b60a082015173ffffffffffffffffffffffffffffffffffffffff166000908152600460209081526040822054908401516bffffffffffffffffffffffff161015611110576040517fcd1c886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604083015173ffffffffffffffffffffffffffffffffffffffff16611161576040517f05bb467c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460a08401516040517fa538b2eb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291169063a538b2eb90602401602060405180830381865afa1580156111d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f99190612985565b61122f576040517f1183afea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360405160200161124291906129a2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301206000818152600590935291205490915073ffffffffffffffffffffffffffffffffffffffff16156112d5576040517f357d0cc400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836000015173ffffffffffffffffffffffffffffffffffffffff16817fd178af9fe30387562e61bb997b245b7f49c26aad1e50c39d7b438ffa6c41b3068660c001518760e00151886060015189604001518a608001518b61012001518c61014001518d61010001518e602001518f60a0015160405161135d9a99989796959493929190612b0e565b60405180910390a3608084015160ff908116600090815260066020526040808220815160608101909252805492936113e09383911660028111156113a3576113a3611fa2565b60028111156113b4576113b4611fa2565b8152905463ffffffff610100820481166020840152650100000000009091041660409091015285611b51565b1561144857608085015160ff166000908152600660205260409020805465010000000000900463ffffffff1690600561141883612bed565b91906101000a81548163ffffffff021916908363ffffffff16021790555050611441858361158a565b90506114ff565b604080516060810182528682015173ffffffffffffffffffffffffffffffffffffffff90811682526020808901516bffffffffffffffffffffffff90811682850190815260a08b01518416858701908152600089815260059094529590922093519151167401000000000000000000000000000000000000000002908216178255915160019091018054919092167fffffffffffffffffffffffff0000000000000000000000000000000000000000919091161790555b949350505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611588576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610aaf565b565b60025482516060840151604080860151608087015160a08801516101008901516101208a01516101408b015195517fc62cf68400000000000000000000000000000000000000000000000000000000815260009973ffffffffffffffffffffffffffffffffffffffff16988a988a9863c62cf6849861161a98939792969095939492939092909190600401612c10565b6020604051808303816000875af1158015611639573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061165d9190612c8f565b90507f000000000000000000000000231d45b53c905c3d6201318156bdc725c9c3b9b173ffffffffffffffffffffffffffffffffffffffff168560a0015173ffffffffffffffffffffffffffffffffffffffff16036117d45760007f000000000000000000000000231d45b53c905c3d6201318156bdc725c9c3b9b173ffffffffffffffffffffffffffffffffffffffff16634000aea08488602001518560405160200161170d91815260200190565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161173a93929190612ca8565b6020604051808303816000875af1158015611759573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177d9190612985565b9050806117ce576040517f39f1c8d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401610aaf565b506118b0565b6118158286602001516bffffffffffffffffffffffff168760a0015173ffffffffffffffffffffffffffffffffffffffff16611bf69092919063ffffffff16565b60208501516040517f948108f7000000000000000000000000000000000000000000000000000000008152600481018390526bffffffffffffffffffffffff909116602482015273ffffffffffffffffffffffffffffffffffffffff83169063948108f790604401600060405180830381600087803b15801561189757600080fd5b505af11580156118ab573d6000803e3d6000fd5b505050505b80847fb9a292fb7e3edd920cd2d2829a3615a640c43fd7de0a0820aa0668feb4c37d4b8760c001516040516118e59190611f57565b60405180910390a3949350505050565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905261194b9084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401611018565b505050565b3373ffffffffffffffffffffffffffffffffffffffff8216036119cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610aaf565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000611aa7826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16611d789092919063ffffffff16565b80519091501561194b5780806020019051810190611ac59190612985565b61194b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610aaf565b60008083516002811115611b6757611b67611fa2565b03611b745750600061070a565b600183516002811115611b8957611b89611fa2565b148015611bbc575073ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604090205460ff16155b15611bc95750600061070a565b826020015163ffffffff16836040015163ffffffff161015611bed5750600161070a565b50600092915050565b801580611c9657506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611c70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c949190612c8f565b155b611d22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e6365000000000000000000006064820152608401610aaf565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905261194b9084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401611018565b60606114ff8484600085856000808673ffffffffffffffffffffffffffffffffffffffff168587604051611dac9190612cf4565b60006040518083038185875af1925050503d8060008114611de9576040519150601f19603f3d011682016040523d82523d6000602084013e611dee565b606091505b5091509150611dff87838387611e0a565b979650505050505050565b60608315611ea0578251600003611e995773ffffffffffffffffffffffffffffffffffffffff85163b611e99576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610aaf565b50816114ff565b6114ff8383815115611eb55781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aaf9190611f57565b60005b83811015611f04578181015183820152602001611eec565b50506000910152565b60008151808452611f25816020860160208601611ee9565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611f6a6020830184611f0d565b9392505050565b803560ff81168114611f8257600080fd5b919050565b600060208284031215611f9957600080fd5b611f6a82611f71565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110612008577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b600060608201905061201f828451611fd1565b602083015163ffffffff8082166020850152806040860151166040850152505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610160810167ffffffffffffffff8111828210171561209857612098612045565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156120e5576120e5612045565b604052919050565b73ffffffffffffffffffffffffffffffffffffffff81168114610f1957600080fd5b8035611f82816120ed565b80356bffffffffffffffffffffffff81168114611f8257600080fd5b803563ffffffff81168114611f8257600080fd5b600082601f83011261215b57600080fd5b813567ffffffffffffffff81111561217557612175612045565b6121a660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161209e565b8181528460208386010111156121bb57600080fd5b816020850160208301376000918101602001919091529392505050565b600061016082840312156121eb57600080fd5b6121f3612074565b90506121fe8261210f565b815261220c6020830161211a565b602082015261221d6040830161210f565b604082015261222e60608301612136565b606082015261223f60808301611f71565b608082015261225060a0830161210f565b60a082015260c082013567ffffffffffffffff8082111561227057600080fd5b61227c8583860161214a565b60c084015260e084013591508082111561229557600080fd5b6122a18583860161214a565b60e0840152610100915081840135818111156122bc57600080fd5b6122c88682870161214a565b8385015250610120915081840135818111156122e357600080fd5b6122ef8682870161214a565b83850152506101409150818401358181111561230a57600080fd5b6123168682870161214a565b8385015250505092915050565b60006020828403121561233557600080fd5b813567ffffffffffffffff81111561234c57600080fd5b6114ff848285016121d8565b60006020828403121561236a57600080fd5b813567ffffffffffffffff81111561238157600080fd5b82016101608185031215611f6a57600080fd5b8015158114610f1957600080fd5b600080604083850312156123b557600080fd5b82356123c0816120ed565b915060208301356123d081612394565b809150509250929050565b600067ffffffffffffffff8211156123f5576123f5612045565b5060051b60200190565b600082601f83011261241057600080fd5b81356020612425612420836123db565b61209e565b82815260059290921b8401810191818101908684111561244457600080fd5b8286015b8481101561245f5780358352918301918301612448565b509695505050505050565b60008060006060848603121561247f57600080fd5b833561248a816120ed565b925060208481013567ffffffffffffffff808211156124a857600080fd5b818701915087601f8301126124bc57600080fd5b81356124ca612420826123db565b81815260059190911b8301840190848101908a8311156124e957600080fd5b938501935b82851015612510578435612501816120ed565b825293850193908501906124ee565b96505050604087013592508083111561252857600080fd5b5050612536868287016123ff565b9150509250925092565b60006020828403121561255257600080fd5b8135611f6a816120ed565b60006020828403121561256f57600080fd5b5035919050565b6000806000806060858703121561258c57600080fd5b8435612597816120ed565b935060208501359250604085013567ffffffffffffffff808211156125bb57600080fd5b818701915087601f8301126125cf57600080fd5b8135818111156125de57600080fd5b8860208285010111156125f057600080fd5b95989497505060200194505050565b60008060006060848603121561261457600080fd5b61261d84611f71565b925060208401356003811061263157600080fd5b915061263f60408501612136565b90509250925092565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261267d57600080fd5b830160208101925035905067ffffffffffffffff81111561269d57600080fd5b8036038213156126ac57600080fd5b9250929050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6020815261272a602082016127108461210f565b73ffffffffffffffffffffffffffffffffffffffff169052565b60006127386020840161211a565b6bffffffffffffffffffffffff81166040840152506127596040840161210f565b73ffffffffffffffffffffffffffffffffffffffff811660608401525061278260608401612136565b63ffffffff811660808401525061279b60808401611f71565b60ff811660a0840152506127b160a0840161210f565b73ffffffffffffffffffffffffffffffffffffffff811660c0840152506127db60c0840184612648565b6101608060e08601526127f3610180860183856126b3565b925061280260e0870187612648565b92507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061010081888703018189015261283c8686856126b3565b955061284a818a018a612648565b95509250506101208188870301818901526128668686856126b3565b9550612874818a018a612648565b95509250506101408188870301818901526128908686856126b3565b955061289e818a018a612648565b955092505080878603018388015250611dff8484836126b3565b600061070a36836121d8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612953576129536128f3565b5060010190565b60ff84168152606081016129716020830185611fd1565b63ffffffff83166040830152949350505050565b60006020828403121561299757600080fd5b8151611f6a81612394565b602081526129c960208201835173ffffffffffffffffffffffffffffffffffffffff169052565b600060208301516129ea60408401826bffffffffffffffffffffffff169052565b50604083015173ffffffffffffffffffffffffffffffffffffffff8116606084015250606083015163ffffffff8116608084015250608083015160ff811660a08401525060a083015173ffffffffffffffffffffffffffffffffffffffff811660c08401525060c08301516101608060e0850152612a6c610180850183611f0d565b915060e08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610100818786030181880152612aaa8584611f0d565b945080880151925050610120818786030181880152612ac98584611f0d565b945080880151925050610140818786030181880152612ae88584611f0d565b908801518782039092018488015293509050612b048382611f0d565b9695505050505050565b6000610140808352612b228184018e611f0d565b90508281036020840152612b36818d611f0d565b905063ffffffff8b16604084015273ffffffffffffffffffffffffffffffffffffffff8a16606084015260ff8916608084015282810360a0840152612b7b8189611f0d565b905082810360c0840152612b8f8188611f0d565b905082810360e0840152612ba38187611f0d565b9150506bffffffffffffffffffffffff8416610100830152612bde61012083018473ffffffffffffffffffffffffffffffffffffffff169052565b9b9a5050505050505050505050565b600063ffffffff808316818103612c0657612c066128f3565b6001019392505050565b600061010073ffffffffffffffffffffffffffffffffffffffff808c16845263ffffffff8b166020850152808a16604085015260ff891660608501528088166080850152508060a0840152612c6781840187611f0d565b905082810360c0840152612c7b8186611f0d565b905082810360e0840152612bde8185611f0d565b600060208284031215612ca157600080fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff841681526bffffffffffffffffffffffff83166020820152606060408201526000612ceb6060830184611f0d565b95945050505050565b60008251612d06818460208701611ee9565b919091019291505056fea164736f6c6343000813000a
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.