Skip to content
Last updated: February 06, 2026

SCWE-148: Gas Exhaustion via Unbounded Loops with External Calls

Stable Version v1.0

This content is in the version-(v1.0) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Relationships

Description

Loops that iterate over user-controlled arrays and perform external calls (transfers, approvals, or other contract calls) in each iteration can exhaust gas when the array is large. Each external call consumes gas; unbounded loops cause the transaction to hit the block gas limit and revert, resulting in denial of service. SCWE-109 covers unbounded loops generally; this weakness focuses on loops that perform external calls.

Remediation

  • Impose an upper bound on loop iterations or batch size.
  • Use pull-based patterns: let users claim individually instead of pushing to many addresses in one tx.
  • Process in chunks with pagination or checkpoints.

Examples

Vulnerable

pragma solidity ^0.8.0;

contract Airdrop {
    function distribute(address[] calldata recipients, uint256 amount) external {
        for (uint256 i = 0; i < recipients.length; i++) {
            (bool ok, ) = recipients[i].call{value: amount}("");
            require(ok, "Transfer failed");
        }
    }
}
Why vulnerable: Large recipients.length causes out-of-gas; one failed transfer reverts the entire batch.

Fixed

uint256 public constant MAX_BATCH = 100;

function distribute(address[] calldata recipients, uint256 amount) external {
    require(recipients.length <= MAX_BATCH, "Batch too large");
    for (uint256 i = 0; i < recipients.length; i++) {
        (bool ok, ) = recipients[i].call{value: amount}("");
        require(ok, "Transfer failed");
    }
}