What does `_disableInitializers();` function mean?

I couldn't find anywhere the meaning of the use of the _disableInitializers(); for the upgradeability aspect of an ERC-20 token creation...

what does it mean ?

Also, why does the OpenZeppelin Documentation says to do the following instead ? :

I am confused on what is meant to be done to get the upgradability working

:computer: Environment

wizard OpenZeppelin Interfac

1 Like

Hi @migbash,

_disableInitializers() was added in Contracts v4.6.0 as part of the reinitializers support. The Caution note in Initializer provides more details on why this is needed.

By putting it in the constructor, this prevents initialization of the implementation contract itself, as extra protection to prevent an attacker from initializing it.

Thank you for mentioning the difference with the upgrades documentation. The contructor() initializer {} usage was recommended for previous version of Contracts. I've opened an issue to update the upgrades documentation to refer to _disableInitializers().

2 Likes

Hello @ericglau , just curious if we can still use constructor() initializer{} to prevent the implementation contract initialization issue after 4.6.0?

@QiLOL It is recommended to use _disableInitializers() so that the implementation cannot be initialized to any version.

If you use constructor() initializer{}, you can see that the initializer modifier only sets the _initialized version to 1, meaning that it could still be possible for the implementation to be reinitialized to a higher version number (if the implementation had a way to do so).

1 Like

@ericglau I see, thank you!

@ericglau could you please elaborate on your answer?

Can a logic contract contain both _disableInitializer constructor and initialize function?

What will be the impact of this?

Because from the Openzeppelin doc, constructor shouldn't be used anymore if the initialize function is in use.

@tibas To summarize, the implementation contract (logic contract) should look similar to the first post. It makes sense to have a both _disableInitializer constructor and initialize function. The constructor is to disable the implementation contract from being initialized, while the initializer function is to allow the proxy to be initialized.

Thank you for the explanation, @ericglau.

Hello, If we are updating the implementation contract (i.e. using implementationV1.upgradeTo(address(implementationV2))) is it still true that we need both the constructor with _disableInitializer AND the initialize function?

I'm little confused here because when running proxy.upgradeTo() function like above, it does not run the initialize function in the V2 implementation (when _disableInitializer is present in the constructor).

@ska123 If the implementationV2 is only meant to be used for an upgrade (meaning you will not be deploying a new proxy with V2 directly), then you are right that you don't need an initializer in V2. You may want to have a reinitializer though to act as a migration function (if your contract needs it), and call that using upgradeToAndCall() so it is called atomically with the upgrade.

1 Like

Ah ok, perfect. Thank you!

@ska123 I don't understand your question. Can you provide more detail on what you are trying to achieve and some minimal code examples?

Are we only supposed to add the _disableInitializers for UUPS Upgradable Implementations??

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

It is recommended to add that for implementations regardless of what type of proxy they are used with.

Can you explain why do we need them for proxies that are not UUPS? Most contracts only include _disableIntiialziers() if the implementation only interacts with UUPS.

It isn't strictly needed, but it is recommended as a best practice.

The original motivation for disabling initializers was to have an extra layer of security related to UUPSUpgradeable Vulnerability Post-mortem.

But doing this also has the benefit of preventing users from using the implementation directly, and helps prevent others from overtaking the implementation in general (there is more risk for UUPS since UUPS has the upgrade logic in the implementation so you really don't want someone to overtake it, but in general the risk depends on your code).

1 Like