The withdraw function in the staking protocol lacks a minimum time check between consecutive withdrawals. As a result, users can execute multiple withdrawal requests in quick succession without any delay.
* @notice Sets the minimum amount of time between calls to performUpkeep to finalize withdrawals
* @param _minTimeBetweenWithdrawals minimum time
*/
function setMinTimeBetweenWithdrawals(uint64 _minTimeBetweenWithdrawals) external onlyOwner {
minTimeBetweenWithdrawals = _minTimeBetweenWithdrawals;
emit SetMinTimeBetweenWithdrawals(_minTimeBetweenWithdrawals);
}
function withdraw(uint256[] calldata _withdrawalIds, uint256[] calldata _batchIds) external {
address owner = msg.sender;
uint256 amountToWithdraw;
for (uint256 i = 0; i < _withdrawalIds.length; ++i) {
uint256 withdrawalId = _withdrawalIds[i];
Withdrawal memory withdrawal = queuedWithdrawals[_withdrawalIds[i]];
uint256 batchId = _batchIds[i];
WithdrawalBatch memory batch = withdrawalBatches[batchId];
if (withdrawalOwners[withdrawalId] != owner) revert SenderNotAuthorized();
if (
batchId != 0 && withdrawalId <= withdrawalBatches[batchId - 1].indexOfLastWithdrawal
) revert InvalidWithdrawalId();
if (
batchId != 0 &&
withdrawalId > batch.indexOfLastWithdrawal &&
withdrawal.partiallyWithdrawableAmount == 0
) revert InvalidWithdrawalId();
if (withdrawalId <= batch.indexOfLastWithdrawal) {
amountToWithdraw +=
withdrawal.partiallyWithdrawableAmount +
(uint256(batch.stakePerShares) * uint256(withdrawal.sharesRemaining)) /
1e18;
delete queuedWithdrawals[withdrawalId];
delete withdrawalOwners[withdrawalId];
} else {
amountToWithdraw += withdrawal.partiallyWithdrawableAmount;
queuedWithdrawals[withdrawalId].partiallyWithdrawableAmount = 0;
}
}
token.safeTransfer(owner, amountToWithdraw);
emit Withdraw(owner, amountToWithdraw);
}
The absence of time constraints allows users to withdraw funds repeatedly, leading to rapid depletion of the withdrawal pool.
Update Last Withdrawal Timestamp: After a successful withdrawal, update the timeOfLastWithdrawal: