Back to Blog
Stack Too Deep and Indexed Event Logs

Stack Too Deep and Indexed Event Logs

ABA|April 26, 2025·Updated February 6, 2025|6 min read|

Executive Overview

  • Solidity events with indexed parameters of complex types (structs, arrays, strings, bytes) require additional compiler-generated code to compute a Keccak256 hash of the data
  • This additional code consumes stack slots and can unexpectedly trigger "stack too deep" errors, even when the original code compiled successfully
  • At the moment of writing this article, the compiler error message does not indicate that the issue originates from event indexing, making it difficult to debug
  • Understanding this behavior is important when designing events for complex data types or when recommending indexed parameters during code reviews

Understanding Events

Events are a fundamental part of the Ethereum ecosystem, serving two primary purposes:

  • A cheaper alternative to storage, provided that the contract itself does not need access to the emitted data
  • A mechanism to trigger off-chain applications listening for specific on-chain activity (events)

Events are triggered (emitted) on-chain by using the emit keyword on a previously declared event. Events can contain indexed parameters, which are stored as separate "topics" in the log entry, enabling efficient filtering and searching within blocks.

Explaining events themselves is beyond the scope of this post. For readers unfamiliar with events, the following resources provide comprehensive explanations:


Solidity Compiler Behavior for Complex Indexed Event Emissions

Solidity documentation defines the following behavior for indexed event parameters:

Indexed event parameters that are not value types, i.e. arrays and structs are not stored directly but instead a Keccak256 hash of an encoding is stored.

In practice, this means that when an event includes an indexed parameter of a complex type such as a struct, array, string, or bytes, the compiler will perform the following steps at runtime:

  • ABI-encode the complex type value
  • Compute the Keccak256 hash of the encoded data
  • Store the resulting hash as a log topic

Crucially, these steps are implemented via compiler-generated code.

This injected logic consumes additional stack slots, and, in functions that already operate near the EVM's 16-slot stack limit, due to local variables, return values, or nested expressions, it can be enough to trigger a stack too deep error.


Hidden Stack Too Deep Errors

When a "stack too deep" error is caused by indexed event parameters, the compiler error message provides no indication that event indexing is the source. The error typically appears without a specific line number or with a misleading location, making debugging difficult.

Stack-too-deep-message

Observing the Compiler-Added Indexing Code

Foundry's inspect command lets you view the intermediate Yul code that the Solidity compiler generates before producing the final bytecode.

Consider a simple contract emitting a struct-based event:

solidity
pragma solidity ^0.8.13;

contract Emitter {

    struct BigStruct {
        uint256 slot1;
        uint256 slot2;
        uint256 slot3;
        uint256 slot4;
        uint256 slot5;
        uint256 slot6;
        uint256 slot7;
        uint256 slot8;
        uint256 slot9;
        uint256 slot10;
    }

    event EmitInfo(BigStruct indexed data);

    function doWork(BigStruct memory data) public {
        emit EmitInfo(data);
    }
}

By running the forge inspect Emitter ir command, once with an indexed struct parameter and once without, we can see exactly what additional code the compiler injects to handle the indexed hashing.

Comparing the intermediate representation (IR) of indexed versus non-indexed versions reveals the following:

indexed event emission (left) vs non-indexed event emission (right)

We can observe that:

  • The indexed version includes an additional function to compute the Keccak256 hash
  • The log1 opcode is replaced with log2 to accommodate the extra topic
  • The hash computation function adds multiple stack operations

Discovered in the Wild

We encountered this issue during the remediation phase of our iLayer LayerZero Integration audit. After applying fixes, the codebase suddenly failed to compile with a stack too deep error that had no obvious source.

To isolate the cause, we resorted to a binary search approach where we systematically commented out sections of the codebase until compilation succeeded, continuing until we narrowed down the responsible code. This eventually pointed to an event emission, which was unexpected since events are not typically associated with stack depth issues.

The only change that had been made to that specific event was the addition of indexed to one of its emitted parameters. Specifically on a parameter of the following Order structure type:

solidity
struct Token {
    Type tokenType;
    bytes32 tokenAddress;
    uint256 tokenId;
    uint256 amount;
}

struct Order {
    bytes32 user;
    bytes32 recipient;
    bytes32 filler;
    Token[] inputs;
    Token[] outputs;
    uint32 sourceChainEid;
    uint32 destinationChainEid;
    bool sponsored;
    uint64 primaryFillerDeadline;
    uint64 deadline;
    bytes32 callRecipient;
    bytes callData;
    uint256 callValue;
}

This prompted a deeper investigation into how the Solidity compiler handles indexed complex types, which led to the findings documented in this article.

To summarize, in our case, adding the indexed keyword to an Order parameter in an event caused the compiler to generate substantial hashing overhead code, which then triggered a significantly-hard-to-debug stack too deep error.


Implications for Developers and Auditors

ScenarioConsideration
Designing eventsAvoid indexing complex types unless filtering by that parameter is genuinely required
Code reviewsRecommendations to add indexing should consider the struct complexity and existing function stack usage
Debugging stack errorsWhen encountering unexplained stack too deep errors, check recent changes to event definitions
Large structsEvents emitting large structs may need to emit individual fields rather than the entire struct

Bonus: Brief Showcase of Event Issues in Web3

Events can be the source of various security and functionality issues beyond stack depth problems. Notably, event-related vulnerabilities appear more frequently in off-chain processing systems, such as bridges or blockchain node software, than in smart contracts themselves. These issues typically require in-depth knowledge to identify and often stem from inadequate validation of log data.

The purpose of this article is not to showcase an exhaustive list of event-related issues, however, the following is selection of notable examples organized by category to illustrate the variety of event-related vulnerabilities that have been discovered over the years.

Insufficient Off-Chain Log Validation

Event Processing in Blockchain Infrastructure

The Zeta blockchain audit revealed multiple event processing vulnerabilities:

Incorrect Event Processing in Off-Chain Systems

Cross-Chain Event Handling

Further Reading

For those interested in further investigating blockchain event-related vulnerabilities:


Conclusion

The Solidity compiler's handling of indexed event parameters for complex types is a subtle but important behavior to understand. When a struct, array, or other non-primitive value type is indexed, the compiler generates additional code to hash the data, which consumes stack space.

Key takeaways:

  • Indexed complex types require compiler-generated hashing code that uses stack slots
  • Stack too deep errors from event indexing are not clearly indicated in compiler output
  • Consider the trade-off between indexing convenience and compilation constraints
  • When debugging unexplained stack errors, review recent event definition changes

While this behavior is documented, it is not widely known, making it a common source of confusion when it manifests as compilation errors in otherwise straightforward code changes.

This article was originally published at https://x.com/abarbatei/status/1916130703181336719