15

I know that this has been covered in other questions here but I'm not sure what's happening in my case. My code is returning a compiler error "Stack too deep, try removing local variables." But I only have around five function arguments and a few more local variables. The function in question is given below:

function sell(bool limit, uint256 whichquery, uint8 whichoutcome, uint256 amount, uint16 price) {
        uint cumulativeamountgained;
        uint numorderstobedeleted;
        uint amountnotfilled = amount;
        if(tokenHoldings[msg.sender][whichquery][whichoutcome] < amount) throw;
        if(limit){
            for (uint i=orderbook[whichquery][whichoutcome][0].length-1;i>=0;i--){
                if(orderbook[whichquery][whichoutcome][0][i][0] >= price) {
                    if(orderbook[whichquery][whichoutcome][0][i][1] > amountnotfilled){
                        orderbook[whichquery][whichoutcome][0][i][1] -= amountnotfilled;
                        tokenHoldings[address(orderbook[whichquery][whichoutcome][0][i][2])][whichquery][whichoutcome] += amountnotfilled;

The error is triggered on the last line of this code snippet under the first "whichquery".

7 Answers 7

16

The limit of having not more than 16 local variables could be a problem here. You can try to refactor your code and probably break this function into two parts. That should fix the issue I hope.

Please read the article Solidity stack too deep by James Carlyle to understand more about this issue.

14

You can also scope variables like this

uint var1;
{
    (uint varA, uint varB) = getVars();
    var1 = varA + varB;
}

// now use var1

Inspired by Uniswap.

1
  • 2
    This worked for me. It's a really nice, clean workaround. Aug 24, 2020 at 15:44
2

Found nice trick to avoid "Stack too deep" issue:

function swapWithReferral(
    IERC20 fromToken,
    IERC20 destToken,
    uint256 amount,
    uint256 minReturn,
    uint256[] calldata distribution,
    uint256 flags,
    address referral,
    uint256 feePercent
) external makeGasDiscount(flags) returns(uint256 returnAmount) {
    ...
    fromToken.transferFrom(address(this), amount); // <-- Stack too deep
    ...
    destToken.transfer(msg.sender, returnAmount);  // <-- Stack too deep
    ...
}

Just define few additional internal functions like _fromToken() and _destToken() to decode args:

// Helps to avoid "Stack too deep" in swap() method
function _fromToken(bytes memory data) internal pure returns(IERC20 token) {
    assembly {
        token := mload(add(data, 36))
    }
}

// Helps to avoid "Stack too deep" in swap() method
function _destToken(bytes memory data) internal pure returns(IERC20 token) {
    assembly {
        token := mload(add(data, 68))
    }
}

And use these functions instead of access to those vars:

function swapWithReferral(
    IERC20 /*fromToken*/,
    IERC20 /*destToken*/,
    uint256 amount,
    uint256 minReturn,
    uint256[] calldata distribution,
    uint256 flags,
    address referral,
    uint256 feePercent
) external makeGasDiscount(flags) returns(uint256 returnAmount) {
    ...
    _fromToken(msg.sender).transferFrom(address(this), amount); // Works great!
    ...
    _destToken(msg.sender).transfer(msg.sender, returnAmount);  // Works great!
    ...
}

Notice: Be careful with internal and public functions, since msg.data is updated only on external calls. You can externally call public function with this.doSomething().

1

there is limited amount of local variables you are allowed to declare in your function(arguments and return types inclusive), i think it is 16, but as it seems you have exceeded that limit.

7
  • Yip. It's 16 fixed length fields. Strings take two slots, so 8 strings. Jul 7, 2017 at 0:08
  • @RobHitchens seems that strings only take up one slot, which I'm assuming is ptr to its memory location, so you can utilize 15 (minus one stack for operations on said values). Apr 7, 2022 at 1:49
  • Moving from local variable declarations to parameters does reduce the number a bit, but I was able to define a function with 11 memory string arguments, which then sequentially assign all to one local mem variable successfully. The parameters are being passed in and stripped of their calldata position pointers in the process of being moved to a memory location (3 words to 2 words). Have to dig in deeper as to why 11 is the magic number. Apr 8, 2022 at 17:01
  • Confirmed. 11 is the new magic number. You might find pertinent information in either EVM fork history after 2017 or the Solidity compiler release history. Possibly something in the previously experimental ABI V2 that is now part of Solidity proper. Perhaps a kind soul will jump in and update this answer. Apr 9, 2022 at 19:40
  • I'm curious now, so I opened a new question about the magic number. ethereum.stackexchange.com/questions/125846/… Apr 9, 2022 at 19:49
0

Good solution is break down function, and make a relation with a specfic value.

My first solution was:

 struct Statement{
        uint256 id;
        uint256 userId;
        uint256 surveyAreaId;
        string latitude;
        string longitude;
        string indicator1;
        string indicator2;
        string indicator3;
        string indicator4;
        string indicator5;
    }

And I tried to all varible inside one function and compiler showed me ""Stack too deep, try removing local variables.""

So I broke down all code like that:

struct UINTStatement{
        uint256 id;
        uint256 userId;
        uint256 surveyAreaId;
    }
    
    mapping(uint256=>UINTStatement) public uintStatements;
    
    struct StringStatement{
        uint256 id;
        string latitude;
        string longitude;
        string indicator1;
        string indicator2;
        string indicator3;
        string indicator4;
        string indicator5;
    }
    
    mapping(uint256=>StringStatement) public stringStatements;

Make different two function to create value.

0

My personal solution for this issue was to select the "Enable Truffle Compilation" option in Remix: https://remix-ide.readthedocs.io/en/latest/truffle.html (I already had Rremix connected to my local files with remixd which is a prerequisite). Somehow my local compiler could resolve it but not the standard browser one.

0

If your function cross 8 parameter it will cause you this error.

Make sure your function have less than 8 parameter it will work.

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.

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