[Ethereum] How to get delegatecall to return data

delegatecallsolidity

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.

Best Answer

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))
}
Related Topic