Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: medium
Valid

Chainlink automation Upkeep can not function because of improper integration

Summary

Chainlink automation Upkeep can not work properly because of wrong integration in contracts PriorityPool, WithdrawalPool

Vulnerability Details

From Chainlink's docs, the function checkUpkeep(bytes calldata checkData) external view override returns (bool upkeepNeeded, bytes memory performData). In case upkeepNeeded returned true, then the performData is used as input for function performUpkeep(bytes calldata performData).

Chainlink upkeep integration from contract PriorityPool: The function checkUpkeep() returns a abi-encoded of an uint256, when the function performUpkeep() tries to decode the input to bytes[]. With the returned values from checkUpkeep(), the function performUpkeep() will always revert.

function checkUpkeep(bytes calldata) external view returns (bool, bytes memory) {
uint256 strategyDepositRoom = stakingPool.getStrategyDepositRoom();
uint256 unusedDeposits = stakingPool.getUnusedDeposits();
if (poolStatus != PoolStatus.OPEN) return (false, "");
if (
strategyDepositRoom < queueDepositMin ||
(totalQueued + unusedDeposits) < queueDepositMin
) return (false, "");
return (
true,
@> abi.encode(
MathUpgradeable.min(
MathUpgradeable.min(strategyDepositRoom, totalQueued + unusedDeposits),
queueDepositMax
)
)
);
}
function performUpkeep(bytes calldata _performData) external {
@> bytes[] memory depositData = abi.decode(_performData, (bytes[]));
_depositQueuedTokens(queueDepositMin, queueDepositMax, depositData);
}

Similarly, the integration in contract WithdrawalPool is improper such that: checkUpkeep() returns empty data if upkeep is needed, when the function performUpkeep() tries to decode input to bytes[] which will always revert

function checkUpkeep(bytes calldata) external view returns (bool, bytes memory) {
if (
_getStakeByShares(totalQueuedShareWithdrawals) != 0 &&
priorityPool.canWithdraw(address(this), 0) != 0 &&
block.timestamp > timeOfLastWithdrawal + minTimeBetweenWithdrawals
) {
@> return (true, "");
}
return (false, "");
}
function performUpkeep(bytes calldata _performData) external {
uint256 canWithdraw = priorityPool.canWithdraw(address(this), 0);
uint256 totalQueued = _getStakeByShares(totalQueuedShareWithdrawals);
if (
totalQueued == 0 ||
canWithdraw == 0 ||
block.timestamp <= timeOfLastWithdrawal + minTimeBetweenWithdrawals
) revert NoUpkeepNeeded();
timeOfLastWithdrawal = uint64(block.timestamp);
uint256 toWithdraw = totalQueued > canWithdraw ? canWithdraw : totalQueued;
@> bytes[] memory data = abi.decode(_performData, (bytes[]));
priorityPool.executeQueuedWithdrawals(toWithdraw, data);
_finalizeWithdrawals(toWithdraw);
}

Impact

Automation tasks can not be done by Upkeep

Tools Used

Manual

Recommendations

Update integration to return and decode data properly

Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Chainlink Automation integration bad

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.