5

I'm having trouble getting delegatecall to return the output of the executed function.

Here is a simplified version that shows my problem.

I have this contract deployed on kovan at: 0x850ec47a0f40e3605a7ba21f1c99b04410090134

pragma solidity ^0.4.8;
contract Tester {
    function getBytes() returns (bytes32) {
        return bytes32(0x1);
    }
}

I want to make a delegatecall to this contract and get the return 0x0000000000000000000000000000000000000000000000000000000000000001

Here is my caller contract:

pragma solidity ^0.4.8;
contract foo {
    function bar() returns (bytes32 response) {
        address dest = 0x850ec47a0f40e3605a7ba21f1c99b04410090134;
        bytes memory calldata = hex"0bcd3b33";
        assembly {
            let status := delegatecall(sub(gas,5000), dest, add(calldata, 0x20), mload(calldata), response, 32)
            jumpi(invalidJumpLabel, iszero(status))
        }
        return response;
    }
}

Now I can call the function bar either via a JSON-RPC call or via a transaction from another contract. The response is always 0x0. I can check on etherscan the transaction is not failing in any way. Should the code being imported getBytes() throw, then status should be false and the jumpi should throw bar().

This does not happen. I considered that the return parameter bytes32 response was in calldata and not memory (despite documentation stating it was in memory), and thus could not be written to. I checked this by getting the free memory pointer through mload(0x40), having delegatecall write to that location, and then copying the output to a state variable in storage. Then I could query the state variable to see if delegatecall had returned a value. Still nothing. I'm not sure what I am doing wrong here.

4

The issue is that the response variable is not a memory pointer, it's a stack variable. When you use it as the pointer for your out data in the delegatecall, you're actually just writing the data to the 0 memory location. This doesn't cause errors because that space is reserved for "scratch space", but it also never loads the result into your return variable.

What you want to do is

assembly {
        let status := delegatecall(sub(gas,5000), dest, add(calldata, 0x20), mload(calldata), 0, 32)
        response := mload(0)
        jumpi(invalidJumpLabel, iszero(status))
}
  • In my actual code, rather than having the calldata as an inline hex literal I pass it in as a parameter. When using it in delegatecall, it functions correctly as a memory pointer. Is the fact that response is a return parameter the reason it is a stack variable rather than a memory pointer, and my input calldata argument is not? Or is it rather because calldata of type bytes is a dynamic type while response bytes32 is a value type that makes the difference between memory pointer and stack variable? – Nik Kunkel Apr 13 '17 at 18:24
  • The reason is that the bytes32 type (like uint or address or any other fixed type smaller than 32 bytes) is inherently a stack variable, whereas memory arrays are represented as a pointer to memory. It's like the difference between an int and a pointer to an array in C – Tjaden Hess Apr 13 '17 at 18:44
  • Ahh that makes a lot of sense, thank you very much for your answer. – Nik Kunkel Apr 14 '17 at 3:33

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.