solidityproxy-patternopenzeppelin

Why are _initializing and isTopLevelCall variables used in Initializable contract of Openzeppelin?


This is abstract contract in the context of Proxy pattern:

abstract contract Initializable {
    bool private _initialized;
    bool private _initializing;

    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

contract UpgradebleTest1 is Initializable {
    uint public x;

    function initialize(uint _x) public initializer {
        x = _x;
    }
}

I don't understand the necessity of _initializing and isTopLevelCall. Is not enough doing control using only _initialized?

Thanks,


Solution

  • The _initializing and isTopLevelCall combination allow for chained calls with the initializer modifier:

    contract UpgradebleTest1 is Initializable {
        uint public x;
    
        function initialize(uint _x) public initializer {
            internalInit(_x);
        }
    
        function internalInit(uint _x) internal initializer {
            x = _x;
        }
    }
    

    Without the _initializing and isTopLevelCall check, the initializer modifier would pass on the first call (initialize()) but fail on the second call (internalInit()).

    modifier initializer() {
        require(!_initialized, "Initializable: contract is already initialized");
        _initialized = true;
    }