1

I need to encode a bunch of variables that are part of an EIP712 message. Unfortunately I hit the stack too deep exception when I added hashParent(swap.parent) :(

bytes32 swapHash = keccak256(
    abi.encode(
        SWAP_TYPEHASH,
        swap.scriptId,
        swap.tokenFrom,
        swap.tokenTo,
        swap.amount,
        swap.user,
        swap.executor,
        swap.chainId,
        hashBalance(swap.balance),
        hashFrequency(swap.frequency),
        hashPrice(swap.price),
        hashRepetitions(swap.repetitions),
        hashParent(swap.parent)
    )
);
CompilerError: Stack too deep, try removing local variables.
44 |                 hashParent(swap.parent)    
   |                            ^^^^

Error HH600: Compilation failed

My first idea was to split the abi.encode in two, but I see that abi.encode('a', 'b') is not the same as abi.encode(abi.encode('a'), abi.encode('b')).

Is there a way to go around this limitation?

Edit:

I've made a pastebin with some example code that can be played with on Remix. the idea is to find a way to write hash2 in a way to return the same result of hash, without passing that many arguments to abi.encode

2
  • Do you really need that many parameters? What type is swap? If it is a struct since you only want to obtain a hash you could do keccak256(abi.encode(swap)). If swap has other fields you don't want you could create another struct that only has needed fields.
    – Ismael
    Commented Feb 2, 2022 at 3:55
  • @Ismael some fields of the struct (balance, frequency, price...) Need to be hashed separately with their own typehash string, that's why I cannot hash it all in 1 go. Commented Feb 2, 2022 at 6:50

3 Answers 3

1

Searching on the internet I found this article that was showing the outputs of the encode and the encodePacked functions.

I thought that abi.encode places bytes32 one after the other, untouched. As the last arguments (the result of the hashBalance, hashFrequency.. functions) are bytes32, I thought I could pack them together and then concatenate!

Testing the hypothesis:

I made some experiments on Remix and it seems that the result is the same!

address private exchange = 0xf8e81D47203A594245E36C48e151709F0C19fBe8;
bytes32 private typehash =  keccak256(abi.encodePacked('SOME VERY LONG STRING AS I NEED TO SEE IF ANYTHING IS BROKEN WITH IT'));   
address private tokenFrom = 0xf8e81D47203A594245E36C48e151709F0C19fBe8;
address private tokenTo = 0xf8e81D47203A594245E36C48e151709F0C19fBe8;
address private user = 0xf8e81D47203A594245E36C48e151709F0C19fBe8;
uint256 private chainId = 42;
bool private enabled = true;
bytes32 private c1 =  keccak256(abi.encodePacked('X'));   
bytes32 private c2 =  keccak256(abi.encodePacked('Y'));   
bytes32 private c3 =  keccak256(abi.encodePacked('Z'));   


function hash() external returns (bytes memory) {
    return abi.encode(exchange, typehash, tokenFrom, tokenTo, user, chainId, enabled, c1, c2, c3);
}

function hash2() external returns (bytes memory) {
    bytes memory m1 =abi.encode(exchange, typehash, tokenFrom, tokenTo, user, chainId, enabled);
    bytes memory m2 = abi.encodePacked(c1, c2, c3);
    return bytes.concat(m1, m2);
}

Final solution:

bytes32 swapHash = keccak256(
    bytes.concat(
        abi.encode(
            SWAP_TYPEHASH,
            swap.scriptId,
            swap.tokenFrom,
            swap.tokenTo,
            swap.amount,
            swap.user,
            swap.executor,
            swap.chainId
        ),
        abi.encodePacked(
            hashBalance(swap.balance),
            hashFrequency(swap.frequency),
            hashPrice(swap.price),
            hashRepetitions(swap.repetitions),
            hashParent(swap.parent)
        )
    )
);
0

That is a tough one if you really need to encode them all. Try encoding 2 parts of those variables separately and then concating them?

bytes32 swapHash = keccak256(
    bytes.concat(
    abi.encodePacked(
        SWAP_TYPEHASH,
        swap.scriptId,
        swap.tokenFrom,
        swap.tokenTo,
        swap.amount,
        swap.user),
    abi.encodePacked(
        swap.executor,
        swap.chainId,
        hashBalance(swap.balance),
        hashFrequency(swap.frequency),
        hashPrice(swap.price),
        hashRepetitions(swap.repetitions),
        hashParent(swap.parent)
    ))
);

Since abi.encodePacked produces bytes arrays without junk memory info, two concated bytes arrays should in theory be the same as the whole thing. Then keccak should give the same result, as I tested it out with simple examples and it worked.

1
  • Thanks! But it didn't work. abi.encodePacked packs everything leaving out padding zeroes, so by doing as you suggested it just gives me a different result than the one I tried to get. I added some example code with various types, so it is easier to spot these issues Commented Feb 2, 2022 at 7:39
0

Neither of the solutions provided worked for me but thank you; it was helpful. this is what worked for me:

bytes32 swapHash = keccak256(
    bytes.concat(
    abi.encode(
        SWAP_TYPEHASH,
        swap.scriptId,
        swap.tokenFrom,
        swap.tokenTo,
        swap.amount,
        swap.user),
    abi.encode(
        swap.executor,
        swap.chainId,
        hashBalance(swap.balance),
        hashFrequency(swap.frequency),
        hashPrice(swap.price),
        hashRepetitions(swap.repetitions),
        hashParent(swap.parent)
    ))
); 

Assumptions: all my variables are function params except the SWAP_TYPEHASH.

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.