How to call initialize on implementation when upgrading with OZ Upgrades?

I'm currently using a Transparent Proxy and writing some tests to make sure that the implementation contract was initialized.

The basic idea is the following:

      //coin is our proxy
      const CoinV2Factory = await ethers.getContractFactory("CoinV2");
      await upgrades.upgradeProxy(
          call: {fn: "initialize", args: ["Coin Name", "COIN"]} 
      const impl2Address = await getImplementationAddress(ethers.provider, coin.address);
      const implementation = await ethers.getContractAt("CoinV2", impl2Address);
      await expect(implementation.initialize("Coin Name", "COIN"));

However, I am getting the error "'Initializable: contract is already initialized'".

Disclaimer: I know initializing the Name/Symbol of the Token does not matter from a security perspective, but just giving a simple example.

There are 3 different concepts here:

  1. Initializing a proxy
  2. Re-initializing a proxy during an upgrade
  3. Initializing the implementation itself to prevent someone else from initializing it

1 and 2 are done by initializers or reinitializers (see

Initializers or reinitializers can be called from deployProxy or upgradeProxy as in your example above -- these happen in the context of the proxy's address.

3 is done by having a constructor like the below, which happens in the context of the implementation. See

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

This sets the initialized version to the maximum value for the implementation itself, which prevents someone from calling an initializer or reinitializer in the context of the implementation address.

1 Like