Solidity Delegatecall – Understanding Function Return Values

delegatecallsolidity

Let's suppose I have a simple delegatecall based relay contract. It looks up the contract implementation from a registry and passes the call to the upgradeable implementation:

contract Relay {

    address public registrarAddr;
    string public name;

    function Relay(address _registrarAddr, string _name) {
        registrarAddr = _registrarAddr;
        name = _name;
    }

    function getImplAddr() constant returns (address) {
        Registrar registrar = Registrar(registrarAddr);
        return registrar.addr(name);
    }

    function() {
        address currentVersion = this.getImplAddr();
        if(!currentVersion.delegatecall(msg.data)) throw;
    }
}

As you see the current function() {} has a special check for delegatecall return values, as this is based on an example code.

The question is, how delegatecall handle return values? Can I modify this delegatecall hook to perform normal web3 contract.call().balanceOf(addr) style functions for reading public values from the implementation contract? Or if I set up a relay contract do I need to have special logic in place to read the data from the contract what I would normally do with call()? Are there any examples of such arrangements?

Best Answer

delegatecall is not supposed to handle return values as it cannot be aware of the length of the other side (unless there would be a dedicated parameter passed to it). Currently, the delegatecall Solidity built-in method does not support supplying the return value size, nor getting the return value out after the call.

According to the documentation it should always return a boolean signaling the execution outcome. It seems the compiler may currently have a problem, see https://github.com/ethereum/solidity/issues/2678.

To get the result out, you will need to know the return value size and use inline assembly:

assembly {
    let returnSize = 32
    calldatacopy(0xff, 0, calldatasize)
    let _retVal = delegatecall(gas, currentVersion, 0xff, calldatasize, 0, returnSize)
    switch _retval case 0 { revert(0,0) } default { return(0, returnSize) }
}
Related Topic