Skip to content

Commit 92785ec

Browse files
author
Francisco de Borja Aranda Castillejo
authored
gas optimizations and minor fixes (#12156)
* gas optimizations and minor fixes * minor fixes II * avoid creating the same struct twice * include all possible corner cases into addToWatchListOrDecomission Signed-off-by: Borja Aranda <[email protected]> --------- Signed-off-by: Borja Aranda <[email protected]>
1 parent 2c624fe commit 92785ec

File tree

1 file changed

+50
-26
lines changed

1 file changed

+50
-26
lines changed

contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol

+50-26
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
7171

7272
bytes32 private constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
7373
bytes32 private constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
74-
uint96 private constant DEFAULT_TOP_UP_AMOUNT_JULES = 9000000000000000000;
75-
uint96 private constant DEFAULT_MIN_BALANCE_JULES = 1000000000000000000;
74+
uint96 private constant DEFAULT_TOP_UP_AMOUNT_JUELS = 9000000000000000000;
75+
uint96 private constant DEFAULT_MIN_BALANCE_JUELS = 1000000000000000000;
7676
IERC20 private immutable i_linkToken;
7777

7878
uint256 private s_minWaitPeriodSeconds;
@@ -142,14 +142,15 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
142142
}
143143
// s_onRampAddresses is not the same length as s_watchList, so it has
144144
// to be clean in a separate loop
145-
for (uint256 idx = 0; idx < s_onRampAddresses.length(); idx++) {
146-
(uint256 key, ) = s_onRampAddresses.at(idx);
145+
for (uint256 idx = s_onRampAddresses.length(); idx > 0; idx--) {
146+
(uint256 key, ) = s_onRampAddresses.at(idx - 1);
147147
s_onRampAddresses.remove(key);
148148
}
149149
for (uint256 idx = 0; idx < addresses.length; idx++) {
150150
address targetAddress = addresses[idx];
151151
if (s_targets[targetAddress].isActive) revert DuplicateAddress(targetAddress);
152152
if (targetAddress == address(0)) revert InvalidWatchList();
153+
if (minBalances[idx] == 0) revert InvalidWatchList();
153154
if (topUpAmounts[idx] == 0) revert InvalidWatchList();
154155
s_targets[targetAddress] = MonitoredAddress({
155156
isActive: true,
@@ -173,6 +174,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
173174
/// in which case it will carry the proper dstChainSelector along with the 0x0 address
174175
function addToWatchListOrDecomission(address targetAddress, uint64 dstChainSelector) public onlyAdminOrExecutor {
175176
if (s_targets[targetAddress].isActive) revert DuplicateAddress(targetAddress);
177+
if (targetAddress == address(0) && dstChainSelector == 0) revert InvalidAddress(targetAddress);
176178
bool onRampExists = s_onRampAddresses.contains(dstChainSelector);
177179
// if targetAddress is an existing onRamp, there's a need of cleaning the previous onRamp associated to this dstChainSelector
178180
// there's no need to remove any other address that's not an onRamp
@@ -182,16 +184,19 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
182184
}
183185
// only add the new address if it's not 0x0
184186
if (targetAddress != address(0)) {
185-
s_onRampAddresses.set(dstChainSelector, targetAddress);
186187
s_targets[targetAddress] = MonitoredAddress({
187188
isActive: true,
188-
minBalance: DEFAULT_MIN_BALANCE_JULES,
189-
topUpAmount: DEFAULT_TOP_UP_AMOUNT_JULES,
189+
minBalance: DEFAULT_MIN_BALANCE_JUELS,
190+
topUpAmount: DEFAULT_TOP_UP_AMOUNT_JUELS,
190191
lastTopUpTimestamp: 0
191192
});
192193
s_watchList.add(targetAddress);
193-
} else {
194-
// if the address is 0x0, it means the onRamp has ben decomissioned and has to be cleaned
194+
// add the contract to onRampAddresses if it carries a valid dstChainSelector
195+
if (dstChainSelector > 0) {
196+
s_onRampAddresses.set(dstChainSelector, targetAddress);
197+
}
198+
// else if is refundant as this is the only corner case left, maintaining it for legibility
199+
} else if (targetAddress == address(0) && dstChainSelector > 0) {
195200
s_onRampAddresses.remove(dstChainSelector);
196201
}
197202
}
@@ -219,16 +224,24 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
219224
uint256 idx = uint256(blockhash(block.number - (block.number % s_upkeepInterval) - 1)) % numTargets;
220225
uint256 numToCheck = numTargets < maxCheck ? numTargets : maxCheck;
221226
uint256 numFound = 0;
227+
uint256 minWaitPeriod = s_minWaitPeriodSeconds;
222228
address[] memory targetsToFund = new address[](maxPerform);
223-
MonitoredAddress memory target;
229+
MonitoredAddress memory contractToFund;
224230
for (
225231
uint256 numChecked = 0;
226232
numChecked < numToCheck;
227233
(idx, numChecked) = ((idx + 1) % numTargets, numChecked + 1)
228234
) {
229235
address targetAddress = s_watchList.at(idx);
230-
target = s_targets[targetAddress];
231-
if (_needsFunding(targetAddress, target.minBalance)) {
236+
contractToFund = s_targets[targetAddress];
237+
if (
238+
_needsFunding(
239+
targetAddress,
240+
contractToFund.lastTopUpTimestamp + minWaitPeriod,
241+
contractToFund.minBalance,
242+
contractToFund.isActive
243+
)
244+
) {
232245
targetsToFund[numFound] = targetAddress;
233246
numFound++;
234247
if (numFound == maxPerform) {
@@ -247,15 +260,24 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
247260
/// @notice tries to fund an array of target addresses, checking if they're underfunded in the process
248261
/// @param targetAddresses is an array of contract addresses to be funded in case they're underfunded
249262
function topUp(address[] memory targetAddresses) public whenNotPaused {
250-
MonitoredAddress memory target;
263+
MonitoredAddress memory contractToFund;
264+
uint256 minWaitPeriod = s_minWaitPeriodSeconds;
251265
uint256 localBalance = i_linkToken.balanceOf(address(this));
252266
for (uint256 idx = 0; idx < targetAddresses.length; idx++) {
253267
address targetAddress = targetAddresses[idx];
254-
target = s_targets[targetAddress];
255-
if (localBalance >= target.topUpAmount && _needsFunding(targetAddress, target.minBalance)) {
256-
bool success = i_linkToken.transfer(targetAddress, target.topUpAmount);
268+
contractToFund = s_targets[targetAddress];
269+
if (
270+
localBalance >= contractToFund.topUpAmount &&
271+
_needsFunding(
272+
targetAddress,
273+
contractToFund.lastTopUpTimestamp + minWaitPeriod,
274+
contractToFund.minBalance,
275+
contractToFund.isActive
276+
)
277+
) {
278+
bool success = i_linkToken.transfer(targetAddress, contractToFund.topUpAmount);
257279
if (success) {
258-
localBalance -= target.topUpAmount;
280+
localBalance -= contractToFund.topUpAmount;
259281
s_targets[targetAddress].lastTopUpTimestamp = uint56(block.timestamp);
260282
emit TopUpSucceeded(targetAddress);
261283
} else {
@@ -271,30 +293,32 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
271293
/// if it is elligible for funding
272294
/// @param targetAddress the target to check
273295
/// @param minBalance minimum balance required for the target
296+
/// @param minWaitPeriodPassed the minimum wait period (target lastTopUpTimestamp + minWaitPeriod)
274297
/// @return bool whether the target needs funding or not
275-
function _needsFunding(address targetAddress, uint256 minBalance) private view returns (bool) {
298+
function _needsFunding(
299+
address targetAddress,
300+
uint256 minWaitPeriodPassed,
301+
uint256 minBalance,
302+
bool contractIsActive
303+
) private view returns (bool) {
276304
// Explicitly check if the targetAddress is the zero address
277305
// or if it's not a contract. In both cases return with false,
278306
// to prevent target.linkAvailableForPayment from running,
279307
// which would revert the operation.
280308
if (targetAddress == address(0) || targetAddress.code.length == 0) {
281309
return false;
282310
}
283-
MonitoredAddress memory addressToCheck = s_targets[targetAddress];
284311
ILinkAvailable target;
285312
IAggregatorProxy proxy = IAggregatorProxy(targetAddress);
286313
try proxy.aggregator() returns (address aggregatorAddress) {
314+
// proxy.aggregator() can return a 0 address if the address is not an aggregator
287315
if (aggregatorAddress == address(0)) return false;
288316
target = ILinkAvailable(aggregatorAddress);
289317
} catch {
290318
target = ILinkAvailable(targetAddress);
291319
}
292320
try target.linkAvailableForPayment() returns (int256 balance) {
293-
if (
294-
balance < int256(minBalance) &&
295-
addressToCheck.lastTopUpTimestamp + s_minWaitPeriodSeconds <= block.timestamp &&
296-
addressToCheck.isActive
297-
) {
321+
if (balance < int256(minBalance) && minWaitPeriodPassed <= block.timestamp && contractIsActive) {
298322
return true;
299323
}
300324
} catch {}
@@ -408,9 +432,9 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter
408432
/// @notice Gets configuration information for an address on the watchlist
409433
function getAccountInfo(
410434
address targetAddress
411-
) external view returns (bool isActive, uint256 minBalance, uint256 topUpAmount) {
435+
) external view returns (bool isActive, uint96 minBalance, uint96 topUpAmount, uint56 lastTopUpTimestamp) {
412436
MonitoredAddress memory target = s_targets[targetAddress];
413-
return (target.isActive, target.minBalance, target.topUpAmount);
437+
return (target.isActive, target.minBalance, target.topUpAmount, target.lastTopUpTimestamp);
414438
}
415439

416440
/// @dev Modifier to make a function callable only by executor role or the

0 commit comments

Comments
 (0)