Solidity – Why Double Delegate Call Doesn’t Work

contract-developmentdelegatecall

To create an upgradeable contract I have to use multiple proxies. The contract applies to Proxy A, then Proxy A applies to Proxy B. But delegatecall doesn't work in this case.

The simplified code of my contracts:

pragma solidity ^0.4.24;


contract A {
    uint256 public value = 100;

    function mul() public {
        value *= 2;
    }
}

contract B {
    uint256 public value = 200;
    address a;

    constructor(address _a) {
        a = _a;
    }

    function delegate() public {
        a.delegatecall(bytes4(keccak256("mul()")));
    }
}

contract C {
    uint256 public value = 500;
    address b;

    constructor(address _b) {
        b = _b;
    }

    function delegate() public {
        b.delegatecall(bytes4(keccak256("delegate()")));
    }
}

delegate() function works only if it called at contract B

Thanks!

Best Answer

When, in C, you do b.delegatecall(, what happens is that:

  • the code used is that of B
  • the storage used is that of C

And B and C have the same storage layout:

  • both the uint value are located on storage slot 0.
  • with address a and address b located on storage slot 1

So now you are executing B's code with C's storage. And you ask a.delegatecall(bytes4(keccak256("mul()")));. What the underlying bytecode does is actually take C's value at storage slot 1, in effect b, and use that as a. So you are about to delegatecall on B again.

And you call mul() on B? There is no such function, so it does nothing. In fact, depending on your version of Solidity, this call would silently fail or not.

It's working as expected :)