6

At some point in my contract I have a public, not Constant function that has more than 16 arguments (24 to be exact). I'm getting an error saying Stack too deep. Try removing local variables

I can't split the function into 2. This would result in 2 transactions, which would not work for what I want to do.

My idea was to pass in a dynamic-size byte array with all of the arguments, and then parse thru that array, extract each argument and do the proper type casting. However, I'm struggling with the implementation.

Is there a simpler way to do this? This seems like a problem that other people would run into. I have thought of passing in a big struct, but I understand you can't call a function externally with internal types.

2

Agree with original answers and I'd like to add the reason why because this may help you find a work around:

Access to the stack is limited to the top end in the following way: It is possible to copy one of the topmost 16 elements to the top of the stack or swap the topmost element with one of the 16 elements below it. All other operations take the topmost two (or one, or more, depending on the operation) elements from the stack and push the result onto the stack. Of course it is possible to move stack elements to storage or memory in order to get deeper access to the stack, but it is not possible to just access arbitrary elements deeper in the stack without first removing the top of the stack.

Solidity docs

So maybe there are items elsewhere in your contract you can move off of the top end temporarily. But you will only have access to 16 total items at once. I used dynamically sized arrays for my parameters to solve this.

5

One possible approach is to use a single dynamic-size byte array as you suggest, another is to group the parameters together by their type, so if you have three arguments of type address you instead pass a single array of addresses, of which you expect the user to send three. That avoids the need to construct then parse out a byte array. But often this will just move the problem from the place where you pass these arguments into your contract to the place where you try to use them.

However, needing all those parameters is often (not always) a sign that the design isn't right. For example, you may be trying to store treat the contract like a database with a lot of data fields that the contract doesn't actually need to do calculations on. This is needlessly expensive, and you're often better just storing the hash of some of that data, or JSON-encoding and storing it on IPFS, then just sending the contract a single parameter for the IPFS address. Even if you need all the data in contract storage for some reason, any fields that the contract doesn't need to do calculations on can be stored together in a single string, and parsed out on the client side when it needs them.

1

To solve this, keep in mind: Function Parameters on Stack. So, if you create an array, the load still is on stack memory. Instead, what you can do it, either use only the required variables, or try to initiate multiple calls with different parameters (address[10]), so send 5 in first call and 5 in second. This will add to transaction, but if you do not want to skip any and don't want to create another function. Group on type, and then send a subset in multiple calls.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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