diff --git a/contracts/src/v0.8/automation/upkeeps/CCIPAutoWatchlist.sol b/contracts/src/v0.8/automation/upkeeps/CCIPAutoWatchlist.sol new file mode 100644 index 00000000000..11cb9babc4d --- /dev/null +++ b/contracts/src/v0.8/automation/upkeeps/CCIPAutoWatchlist.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.19; + +import "../../shared/access/ConfirmedOwner.sol"; +import {AutomationCompatibleInterface} from "../interfaces/AutomationCompatibleInterface.sol"; +import {AccessControl} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/access/AccessControl.sol"; +import {LinkAvailableBalanceMonitor} from "../upkeeps/LinkAvailableBalanceMonitor.sol"; +import {ILogAutomation, Log} from "../interfaces/ILogAutomation.sol"; + +contract CCIPOnRampAutoWatchlist is ILogAutomation, ConfirmedOwner { + event setWatchlistOnMonitor(uint64 indexed _dstChainSelector, address indexed _onRamp); + + LinkAvailableBalanceMonitor public linkAvailableBalanceMonitorAddress; + address public routerAddress; + + // keccak256 signature for OnRampSet event set as constant + bytes32 private constant EVENT_SIGNATURE = 0x1f7d0ec248b80e5c0dde0ee531c4fc8fdb6ce9a2b3d90f560c74acd6a7202f23; + + constructor(address contractaddress, address _routerAddress) ConfirmedOwner(msg.sender) { + linkAvailableBalanceMonitorAddress = LinkAvailableBalanceMonitor(contractaddress); + routerAddress = _routerAddress; + } + + // Option to change the Balance Monitor Address + function setBalanceMonitorAddress(address _newBalMonAddress) external onlyOwner { + linkAvailableBalanceMonitorAddress = LinkAvailableBalanceMonitor(_newBalMonAddress); + } + + // Option to change the Router Address + // Changing Router Address would also require setting up new Upkeep as Upkeep is linked with the Router and cannot be changed + function setRouterAddress(address _newRouterAddress) external onlyOwner { + routerAddress = _newRouterAddress; + } + + // updateWatchList function takes emitted OnRamp Address and dstChainSelector as input and calls addToWatchListOrDecommission function of LinkAvailableBalanceMonitor passing these paraments to add the OnRamp address to the watchlist + // addToWatchListOrDecommission function of LinkAvailableBalanceMonitor is used for sanity check before adding the address on the watchlist + // updateWatchList Function is set to internal for security purposes + function updateWatchList(address _targetAddress, uint64 _dstChainSelector) internal { + linkAvailableBalanceMonitorAddress.addToWatchListOrDecommission(_targetAddress, _dstChainSelector); + } + + function checkLog( + Log calldata log, + bytes memory checkData + ) external view override returns (bool upkeepNeeded, bytes memory performData) { + // Ensure Router address is set + require(routerAddress != address(0), "Router address not set"); + + // Check if the log source matches router contract and topics contain the event signature + if (log.source == routerAddress && log.topics.length > 0 && log.topics[0] == EVENT_SIGNATURE) { + // Extract the indexed parameter from the log + uint64 destChainSelector = uint64(uint256(log.topics[1])); // cast to uint64 + address onRamp = address(uint160(uint256(bytes32(log.data)))); // extract from log.data + + // No sanity checks necessary as the would on to relay information to LinkAvailableBalanceMonitor as it is + // Checking is enabled in LinkAvailableBalanceMonitor contract + return (true, abi.encode(destChainSelector, onRamp)); + } + + // If the event signature doesn't match or log source is not Router contract, no upkeep is needed + return (false, ""); + } + + function performUpkeep(bytes memory performData) external override { + // Decode the data received from checkLog + (uint64 destChainSelector, address onRamp) = abi.decode(performData, (uint64, address)); + // Perform the necessary upkeep actions based on the decoded data + updateWatchList(onRamp, destChainSelector); + // Emitting setWatchlist event to track the last added OnRamp addresses in form of Logs + emit setWatchlistOnMonitor(destChainSelector, onRamp); + } +} diff --git a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol index 5d8a8d58c8d..ad64c3e8d0e 100644 --- a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol +++ b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol @@ -44,6 +44,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter event UpkeepIntervalSet(uint256 oldUpkeepInterval, uint256 newUpkeepInterval); event MaxCheckSet(uint256 oldMaxCheck, uint256 newMaxCheck); event MaxPerformSet(uint256 oldMaxPerform, uint256 newMaxPerform); + event MinPerformSet(uint256 oldMinPerform, uint256 newMinPerform); event MinWaitPeriodSet(uint256 s_minWaitPeriodSeconds, uint256 minWaitPeriodSeconds); event TopUpBlocked(address indexed topUpAddress); event TopUpFailed(address indexed recipient); @@ -77,6 +78,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter uint256 private s_minWaitPeriodSeconds; uint16 private s_maxPerform; + uint16 private s_minPerform; uint16 private s_maxCheck; uint8 private s_upkeepInterval; @@ -105,6 +107,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter IERC20 linkToken, uint256 minWaitPeriodSeconds, uint16 maxPerform, + uint16 minPerform, uint16 maxCheck, uint8 upkeepInterval ) { @@ -114,6 +117,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter i_linkToken = linkToken; setMinWaitPeriodSeconds(minWaitPeriodSeconds); setMaxPerform(maxPerform); + setMinPerform(minPerform); setMaxCheck(maxCheck); setUpkeepInterval(upkeepInterval); } @@ -333,7 +337,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter bytes calldata ) external view override whenNotPaused returns (bool upkeepNeeded, bytes memory performData) { address[] memory needsFunding = sampleUnderfundedAddresses(); - if (needsFunding.length == 0) { + if (needsFunding.length <= s_minPerform) { return (false, ""); } uint96 total_batch_balance; @@ -389,6 +393,12 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter s_maxPerform = maxPerform; } + /// @notice Update s_minPerform + function setMinPerform(uint16 minPerform) external onlyRole(ADMIN_ROLE) { + s_minPerform = minPerform; + emit MinPerformSet(s_minPerform, minPerform); + } + /// @notice Update s_maxCheck function setMaxCheck(uint16 maxCheck) public onlyRole(ADMIN_ROLE) { emit MaxCheckSet(s_maxCheck, maxCheck); @@ -412,6 +422,10 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter function getMaxPerform() external view returns (uint16) { return s_maxPerform; } + /// @notice Gets minPerform + function getMinPerform() external view returns (uint16) { + return s_minPerform; + } /// @notice Gets maxCheck function getMaxCheck() external view returns (uint16) {