diff --git a/contracts/.changeset/nasty-llamas-prove.md b/contracts/.changeset/nasty-llamas-prove.md new file mode 100644 index 00000000000..c3b26c9be36 --- /dev/null +++ b/contracts/.changeset/nasty-llamas-prove.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +DEVSVCS-147: add a reentrancy guard for balance monitor diff --git a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol index 5d8a8d58c8d..824bce711bf 100644 --- a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol +++ b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol @@ -61,6 +61,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter error InvalidWatchList(); error InvalidChainSelector(); error DuplicateAddress(address duplicate); + error ReentrantCall(); struct MonitoredAddress { uint96 minBalance; @@ -94,6 +95,8 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter /// whenever a new one is deployed with the same dstChainSelector. EnumerableMap.UintToAddressMap private s_onRampAddresses; + bool private reentrancyGuard; + /// @param admin is the administrator address of this contract /// @param linkToken the LINK token address /// @param minWaitPeriodSeconds represents the amount of time that has to wait a contract to be funded @@ -116,6 +119,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter setMaxPerform(maxPerform); setMaxCheck(maxCheck); setUpkeepInterval(upkeepInterval); + reentrancyGuard = false; } /// @notice Sets the list of subscriptions to watch and their funding parameters @@ -259,7 +263,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter /// @notice tries to fund an array of target addresses, checking if they're underfunded in the process /// @param targetAddresses is an array of contract addresses to be funded in case they're underfunded - function topUp(address[] memory targetAddresses) public whenNotPaused { + function topUp(address[] memory targetAddresses) public whenNotPaused nonReentrant { MonitoredAddress memory contractToFund; uint256 minWaitPeriod = s_minWaitPeriodSeconds; uint256 localBalance = i_linkToken.balanceOf(address(this)); @@ -457,6 +461,13 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter _; } + modifier nonReentrant() { + if (reentrancyGuard) revert ReentrantCall(); + reentrancyGuard = true; + _; + reentrancyGuard = false; + } + /// @notice Pause the contract, which prevents executing performUpkeep function pause() external onlyRole(ADMIN_ROLE) { _pause();