ERC20 Wizard, Upgradeable, Constructor, _disableInitializers()

I spent some time trying to understand the constructor code that is added when using the contracts wizard to create an Upgradeable ERC20 contract.

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

Basically, the premise is to not leave a deployed upgradeable contract uninitialized, since it can be exploited by Dark Forest creatures.

So using hardhat node I used the deployProxy provided by Openzeppelin Hardhat Upgrades API, trying to see when the constructor is invoked.

  1. Default initialize() function is invoked when deployed, constructor not invoked - contract is initialized (Contract safe):
  const mytoken = await upgrades.deployProxy(MyToken, { initializer: 'initialize', kind: 'uups' });
or
  const mytoken = await upgrades.deployProxy(MyToken, { kind: 'uups' });
  1. Deployed contract is not initialized, constructor is not invoked, and manual initialization is required (Contract not safe) :
  const mytoken = await upgrades.deployProxy(MyToken, { initializer: false, kind: 'uups' });
  1. The only way I could get the constructor to be executed was with a non-proxy deployment:
  const mytoken = await MyToken.deploy();

This set the _initialed variable to 255, so no further initalizations are possible.

So an upgradeable wizard-generated contract can be left uninitialized when deployed, or deployed with a non-default initializer function name, but it has to be done intentionally, and hopefully with a plan or code to prevent exploitation.

Cheers

1 Like