Skip to content

Latest commit

 

History

History
65 lines (42 loc) · 2.17 KB

045.md

File metadata and controls

65 lines (42 loc) · 2.17 KB

Ancient Vanilla Dog

Medium

DoS in batch operation in the Launch::batchRefund

Summary

In the loop of batchRefund, each iteration processes a participation ID. The loop uses calldata for the array, which is efficient. But since it's loop over an array provided by the caller, there's a potential risk of gas limits if the array is too large. For example, if the array has hundreds or thousands of entries, the transaction could run out of gas and revert. This prevents any withdrawals, even when all launch groups are legitimately completed.

Root Cause

https://github.com/sherlock-audit/2025-02-rova/blob/main/rova-contracts/src/Launch.sol#L509

@>>    for (uint256 i = 0; i < launchParticipationIds.length; i++) {     // unbounded loops
@>>    ParticipationInfo storage info = launchGroupParticipations[launchParticipationIds[i]]; 
            _processRefund(launchGroupId, launchParticipationIds[i], info);
        }

Now looking at how the ParticipationInfo is retrieved. The code uses storage for the info struct. So each iteration accesses storage directly. Since storage operations are expensive, this could contribute to high gas costs, especially in large batches which can make the transacton reverts.

Internal Pre-conditions

No response

External Pre-conditions

No responseNo resp

Attack Path

No response

Impact

Funds become permanently stuck in the contract as withdrawals are blocked when all launch group are completed.

PoC

No response

Mitigation

Implement a maximum batch size.

+   uint256 MAX_BATCH_SIZE = 100;

 function batchRefund(bytes32 launchGroupId, bytes32[] calldata launchParticipationIds)
        external
        onlyRole(OPERATOR_ROLE)
        nonReentrant
        whenNotPaused
        onlyLaunchGroupStatus(launchGroupId, LaunchGroupStatus.COMPLETED)
    {
+   require(launchParticipationIds.length <= MAX_BATCH_SIZE, "Batch too large"
        for (uint256 i = 0; i < launchParticipationIds.length; i++) {
            ParticipationInfo storage info = launchGroupParticipations[launchParticipationIds[i]];
            _processRefund(launchGroupId, launchParticipationIds[i], info);
        }
    }