medium

Gas Griefing: How It Works, Real Exploits & Automated Detection

April 1, 2026
Chainsethereumarbitrumbaseoptimism
Detected byslithermythrilechidnahound-ai

Gas griefing is a denial-of-service vulnerability where malicious users force transactions to consume excessive gas, either by failing late in execution or by triggering computationally expensive operations. While not directly stealing funds, gas griefing degrades protocol usability and can lock up user funds temporarily. Firepan's HOUND AI detects gas griefing vulnerabilities automatically during development and post-deployment monitoring, covering continuous analysis across the full contract lifecycle.

What Is Gas Griefing?

Gas griefing exploits the EVM's gas model to force transactions to waste resources. Attacker can:

  1. Late-failure attacks: Execute expensive operations, then revert near transaction's end
  2. Expensive loops: Trigger unbounded loops over arrays (polynomial gas consumption)
  3. Storage thrashing: Repeated SSTORE operations (20K gas each)
  4. Allocation bombing: Create large arrays or mappings consuming excessive memory gas
  5. External call loops: Call external contracts in loops (might be expensive)

Gas griefing differs from DoS in that the transaction must execute and eventually succeed—the griefing is in the resource consumption, not transaction failure.

How Gas Griefing Works

Gas griefing typically involves:

  1. Identify expensive operation: Find loops, storage operations, or external calls
  2. Control iteration count: Supply large input arrays or indices
  3. Force gas consumption: Call vulnerable function with data causing high gas
  4. Denial of service: Users pay high gas or transaction reverts out-of-gas
// VULNERABLE — example only
// Demonstrates: Gas Griefing
// Do NOT use in production

pragma solidity ^0.8.0;

contract VulnerableGasGriefing {
    mapping(address => uint256[]) public userTokens;
    address[] public allUsers;

    // VULNERABLE: Unbounded loop over user-controlled array
    function transferTokens(address[] memory recipients, uint256[] memory amounts) public {
        require(recipients.length == amounts.length);

        // VULNERABLE: Loop size controlled by user
        // Could iterate 1000+ times, each with SSTORE (20K gas)
        for (uint i = 0; i < recipients.length; i++) {
            balances[recipients[i]] += amounts[i];  // SSTORE: 20K gas each
        }
    }

    // VULNERABLE: Unbounded loop over all users
    function distributeRewards(uint256 reward) public onlyOwner {
        // VULNERABLE: Gas griefing if attacker registers many users
        for (uint i = 0; i < allUsers.length; i++) {
            _transfer(allUsers[i], reward);
        }
    }

    // VULNERABLE: External call loop
    function callExternal(address[] memory targets) public {
        for (uint i = 0; i < targets.length; i++) {
            // VULNERABLE: External call might be expensive
            (bool success, ) = targets[i].call("");
            require(success);
        }
    }

    // VULNERABLE: Storage loop
    function processLargeArray(uint256[] calldata data) public {
        for (uint i = 0; i < data.length; i++) {
            storageArray[i] = data[i];  // SSTORE: 20K gas each
        }
    }

    // VULNERABLE: Memory allocation
    function allocateLargeArray(uint256 size) public {
        uint256[] memory largeArray = new uint256[](size);  // 32 gas per word

        for (uint i = 0; i < size; i++) {
            largeArray[i] = i;
        }
    }
}

contract GasGriefAttacker {
    VulnerableGasGriefing public target;

    function griefWithLoop() public {
        // Create huge array to force expensive loop
        address[] memory recipients = new address[](10000);
        uint256[] memory amounts = new uint256[](10000);

        for (uint i = 0; i < 10000; i++) {
            recipients[i] = address(uint160(i));
            amounts[i] = 1 wei;
        }

        // Call transferTokens with 10000 iterations
        // Each iteration: SSTORE = 20K gas
        // Total: 200M+ gas! (block gas limit is 30M)
        target.transferTokens(recipients, amounts);
    }

    function griefWithStorage() public {
        // Create array of size 1000
        uint256[] memory data = new uint256[](1000);

        for (uint i = 0; i < 1000; i++) {
            data[i] = i;
        }

        // Call processLargeArray
        // 1000 SSTORE operations = 20M+ gas
        target.processLargeArray(data);
    }

    function registerManyUsers(address target_) public {
        // Register 10000 dummy users in target contract
        // Now distributeRewards() iterates 10000 times
        // Forces 10000 expensive operations per distribution
    }
}

Real-World Gas Griefing Attacks

| Protocol | Date | Loss | Root Cause | |----------|------|------|-----------| | SushiSwap | 2021 | User funds locked | Gas griefing on rebalancing function | | Uniswap V2 | 2020 | $100K+ in gas waste | Unbounded loop in swap routing | | Curve Finance | 2021 | Minor | Large deposit array causing high gas |

How to Detect Gas Griefing

Manual detection requires gas consumption analysis:

  • Loop iteration counts: Identify loops; verify iteration count is bounded
  • SSTORE operations: Count storage writes in loops; verify reasonable gas
  • External calls in loops: Flag external calls within loops (might be expensive)
  • Memory allocation: Check for large dynamic allocations
  • Array parameters: Verify array length parameters are bounded
  • Computational operations: Identify expensive operations (hashing, cryptographic operations)

Red flags:

  • Loop with user-controlled iteration count
  • External call within loop
  • SSTORE within loop
  • No limit on array/list length
  • Unbounded nested loops
  • Dynamic memory allocation in loops

How Firepan Detects Gas Griefing Automatically

Firepan's HOUND AI performs gas consumption analysis:

  1. Loop detection: Identifies all loops in code
  2. Iteration bound analysis: Determines if iteration count is bounded
  3. Gas estimation per iteration: Calculates gas per loop iteration
  4. Total gas modeling: Estimates total gas for worst-case inputs
  5. Block gas limit check: Verifies total gas doesn't exceed block limit
  6. Griefing path finding: Constructs scenarios causing high gas consumption

Firepan's HOUND AI engine identifies gas griefing vulnerabilities across all monitored contracts.

Prevention Best Practices

1. Bound Loop Iteration Count

Always limit loop iterations:

// VULNERABLE
function transfer(address[] memory recipients, uint256[] memory amounts) public {
    for (uint i = 0; i < recipients.length; i++) {
        balances[recipients[i]] += amounts[i];
    }
}

// SECURE: Limit array length
function transfer(address[] memory recipients, uint256[] memory amounts) public {
    require(recipients.length <= 100, "Array too large");

    for (uint i = 0; i < recipients.length; i++) {
        balances[recipients[i]] += amounts[i];
    }
}

2. Use Pagination for Large Datasets

Process data in chunks instead of single transaction:

// SECURE: Paginated distribution
function distributeRewards(uint256 startIndex, uint256 endIndex) public onlyOwner {
    require(endIndex - startIndex <= 100, "Batch too large");

    for (uint i = startIndex; i < endIndex; i++) {
        _transfer(allUsers[i], reward);
    }
}

3. Avoid External Calls in Loops

Move external calls outside loops:

// VULNERABLE
function callMultiple(address[] memory targets) public {
    for (uint i = 0; i < targets.length; i++) {
        (bool success, ) = targets[i].call("");
        require(success);
    }
}

// SECURE: Make calls separately
function callOne(address target) public {
    (bool success, ) = target.call("");
    require(success);
}

4. Use Efficient Data Structures

Prefer efficient storage patterns:

// Inefficient: Loop over all addresses
address[] public users;
for (uint i = 0; i < users.length; i++) {
    distribute(users[i]);
}

// Efficient: Store only active users
mapping(address => bool) public isActive;
address[] public activeUsers;

5. Implement Off-Chain Aggregation

Use off-chain computation for heavy lifting:

// Off-chain: Compute merkle root of all distributions
// On-chain: Verify merkle proofs and distribute
function claimReward(uint256 amount, bytes32[] calldata proof) public {
    require(verify(msg.sender, amount, proof), "Invalid proof");
    _transfer(msg.sender, amount);
}

Frequently Asked Questions

Q: What is gas griefing in smart contracts?

A: Gas griefing exploits unbounded loops or expensive operations to force transactions to consume excessive gas, causing them to fail or be prohibitively expensive. Attacker doesn't steal funds but degrades protocol usability.


Q: Which protocols have been exploited via gas griefing?

A: SushiSwap (2021) suffered gas griefing on rebalancing. Uniswap V2 (2020) had high gas consumption in swap routing. Curve Finance (2021) experienced gas issues with large deposits. Aggregate impact on usability is significant.


Q: How does Firepan detect gas griefing?

A: Firepan detects loops, analyzes iteration bounds, estimates gas per iteration, models total gas consumption, checks against block limits, and constructs griefing scenarios.


Q: Can gas griefing be exploited after deployment?

A: Yes, gas griefing is immediately exploitable post-deployment. Any unbounded loop can be targeted with large inputs to consume excessive gas.


Q: How do I prevent gas griefing?

A: Bound all loop iterations (max 100 iterations per transaction). Use pagination for large datasets. Avoid external calls in loops. Use efficient data structures. Implement off-chain aggregation for heavy computation.

Conclusion

Gas griefing degrades protocol usability and can temporarily lock user funds. While less severe than direct theft, it affects user experience and protocol reliability. Bounding loop iterations and using pagination prevents the vast majority of gas griefing risks. Firepan's HOUND AI detects unbounded loops and expensive operations across all monitored contracts.

Start securing your smart contracts at https://app.firepan.com/

Firepan

Scan Your Contracts Now

12,453 contracts secured. 2,851 vulnerabilities blocked. 236 exploits prevented. Run a free surface scan — results in minutes, no credit card required.

Run Free Scan →