4

I have a quite big function with 16+ local variables which produces the infamous Stack too deep error. To handle this bad boy I have searched for the possible solutions and ended up using scopes.

According to the answer of goodvibration here, Scoping a piece of code with { ... } has the same impact as placing that piece of code in a separate function. This makes sense and let me assume that it is a trivial topic and I shouldn't overthink it. However I still have some questions and would be great to learn the answer about them because there is not so much information on the topic yet.

Let's say I have a function which contains multiple local variables and some function calls inside and outside the newly added scope like this:

  function scopingExample(
                          uint intVar1,
                          address[] calldata arr1,
                          address[] calldata arr2,
                          address[] calldata arr3,
                          address[] calldata arr4,
                          ) external {

    // variables that I need inside and outside the scoped snippet
    address addr1 = arr1[1];
    address addr2 = arr1[2];
    address addr3 = arr1[3];
    address addr4 = arr1[4];
    
    {
    // scoped code snippet to avoid stack too deep
    // local variable inside the scope  
    address scopeVar = arr1[5];
    // function calls 
    functionCallToWriteBlockchain(addr1,arr2[0],arr2[1],arr2[2],scopeVar,amount);
    functionCallToWriteBlockchain(addr2,arr3[0],arr3[1],arr3[2],scopeVar,amount);
    functionCallToWriteBlockchain(addr3,arr4[0],arr4[1],arr4[2],scopeVar,amount);
    }

    otherCallWhichWritesBlockchain(addr1,addr2,addr3,addr4);

  }
  • Is there a downside calling state changing internal functions from inside {...} block? Or this solution just simply behaves as it was described in the above linked answer? Like a function call inside the function.

  • Function arguments, and variables that are declared before the scoping block can be all passed into the scope and will behave like the arguments of that scope/function?

  • If I have 16 variables to handle, which is better, creating two scopes with 2 x 8, or 4 with 4x4 variables, or just one with 1 x 16? I assume having 4 {...} block is 4x work for the EVM compared to the solution where the block scope is maxed out.


For people who are trying to understand and fix the issue as I do, here are the resources I found useful:

Uniswap V2 Contract question: Stack too deep

Error while compiling: Stack too deep

Stack Too Deep Three words of horror

What does scope do in the function itself?

3
  • 2
    I don't think there's a noticiable "downside" it just works. The compiler has to add a few opcodes to adjust the stack, but they are pretty cheap. Regarding scopes, the only way to determine the which is "better" is to measure gas consumption in the real functions. I really hate having too many local variables, even in non-smart contract languages, I'd use an struct or something similar, perhaps split the function into smaller ones.
    – Ismael
    Jun 24, 2022 at 17:00
  • @Ismael After playing with it a few hours my experience is that this can help in some cases, but in complicated situations other data types should be used as you said. I could use one added scope per function and it didn't want to work in functions with if statements. If someone is ok with these limitations it's a quick fix.
    – rihekopo
    Jun 24, 2022 at 19:57
  • Just remember that variables declared in the block scope will not be available outside that scope, so be sure to set the block around all references to any vars it creates.
    – Cliff Hall
    Sep 14, 2022 at 17:51

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.