Is my existing implementation initializable?

Is my implementation contract initializable?

With regards to initializing the implementation contract, within my initialize function, I have these functions and modifiers:

function initialize(   
        address _contractOwner
    ) external initializer {
        __Ownable_init();
        _initializeEIP712();
      ...
    }
// supposed to be called once while initializing.
    // one of the contracts that inherits this contract follows proxy pattern
    // so it is not possible to do this in a constructor
    function _initializeEIP712() internal initializer {
        _setDomainSeperator();
    }
  modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

Since there is _initializeEIP712() which also includes an initialize modifier, I cannot call initialize on an already deployed implementation contract. Would that mean that my implementation contract is protected and a malicious actor will be unable to initialize the implementation contract?

Additional context: I tried initializing the implementation contract (currently initialized==0), but I receive an error message of 'Initializable: contract is already initialized' when I try to initialize the implementation contract (for security purposes). I believe that it is due to having _initializeEIP712(); within the initialize function.

From my understanding this is the reason why the implementation contract above will not be initializable.

  1. The first initialize will have (isTopLevelCall && _initialized < 1) as true since it is a top level call and initialized is 0.

  2. And the second initialize within _initializeEIP712() will return the error "Initializable: contract is already initialized" because it is not true for both cases (since the implementation contract is deployed and isContract(address(this)) will return true.

Note: The proxy can be deployed and initialized using hardhat upgrades.deployProxy() just that the implementation contract cannot be initialized.

Some kind inputs from the experts here will be helpful. Thanks.

@WilsonOfTheJungle Parent contracts should use the onlyInitializing modifier. See https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializers for an example.

Those initializers still only run in the context of the proxy. For the implementation contract's context, you should still have a constructor to lock the implementation from initialization. As of Contracts v4.6.0, the recommended way is:

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

@ericglau thanks for the response.

Understand the best practices and that parent contracts should use onlyInitializing modifier and to use _disableInitializers() within the constructor.

But currently, the implementation contract with the following initialize function is deployed and from a security perspective, I would want to initialize the implementation contract.

So, I would just like to know if the implementation contract is initializable. From my understanding, as _initializeEIP712(); is present within initialize(), can I safely say that the implementation contract is un-intializable so it is 'safe'?

Your assessment sounds correct to me. You can try calling initialize() on the implementation and it should revert as you described.