1

I have a question on how the EVM stack behaves between calls to foreign contracts. Is it temporarily swapped to memory or does each contract account have its own stack space?

The following code examples are chosen to visualize my question and are not real-world examples. They are chosen to reproduce and focus on the core question.

Let's assume I have the following contract:

contract Caller {
    uint result = 0;

    function test() public {
        (uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h) = (1,1,1,1,1,1,1,1);
        result = a + b + c + d + e + f + g + h;
    }
}

Basically, I am putting a bunch of items onto the stack. I chose this redundant version including the storing into a contract variable to avoid the optimizer to optimize the stack, since this question is about stack boundaries.

Now, when I put an additional element onto the stack, I will get the Stack too deep error message from the compiler:

contract Caller {
    uint result = 0;

    function test() public {
        (uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h, uint i) = (1,1,1,1,1,1,1,1,1);
        result = a + b + c + d + e + f + g + h + i;
    }
}

Now, I extract the "stack overload" to a foreign contract and even extend it by one element:

contract Callee {
    uint256 result = 0;

    function call() public returns (uint256) {
        (uint i, uint j) = (1,1);
        result = i + j;
        return result;
    }
}

contract Caller {
    uint result = 0;

    function test(address callee) public {
        (uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h) = (1,1,1,1,1,1,1,1);
        result = a + b + c + d + e + f + g + Callee(callee).call();
    }
}

This compiles and runs properly.

So the question is: how does the stack behave between contract calls? I always thought that when you submit a valid transaction to a smart contract, it will spin up a temporary EVM to execute the commands that are stored inside the contract accounts, store all state modifications in the storage section of the corresponding contract accounts and then tear down the EVM after the transaction terminates. As I understood, the stack and memory in this procedure do not belong to a specific contract account, but to the EVM itself. Thus, I am curious how you can potentially "overload" the stack between calls to other contracts. I guess there are only two possible answers to this question:

  • stack and memory do not belong to the EVM, but each individual contract account has its own stack and memory space where it stores temporary data during execution
  • the current stack is temporarily swapped to memory during the call to a remote contract Anyone can help me with this? Additional documentation would be welcome as well.

1 Answer 1

2

Stack and memory are part of the call. Every call has its own stack and memory, which start from empty.

So, using your words, stack and memory belong to the EVM, not to the contract. If you have a transaction like A -> B -> A (with A and B contracts), the second A won't share stack and memory with the first A. At the same time, the EVM keeps track of all stack and memory of every call separately, so they don't mix up.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.