wighawag / hardhat-deploy

hardhat deployment plugin
MIT License
1.18k stars 286 forks source link

`hardhat deploy` fails when running the 2nd time with upradeable proxy deployments #356

Open mistersingh179 opened 1 year ago

mistersingh179 commented 1 year ago

hardhat deploy runs all the deployment files every time it is run. When I run it the second time it runs a previously ran deploy command which tried to upgrade the proxy to a previous deployed implementation and thus fails as it has already been initialized to a higher version.

Take this code in 00_deploy_box.js

await deploy('Box', {
    contract: "BoxUpgradable",
    from: deployer, // address that will perform the transaction.
    args: [], // normally constructor args, but now argss to initialize
    log: true,
    proxy: {
      proxyContract: 'ERC1967Proxy', // default to "EIP173Proxy"
      proxyArgs: ['{implementation}', '{data}'],
      execute: {
        init: {
          methodName: 'initialize',
          args: [deployer]
        }
      }
    }
  });

await deploy('Box', {
    contract: "Box2Upgradable",
    from: deployer, // address that will perform the transaction.
    args: [], // normally constructor args, but now argss to initialize
    log: true,
    proxy: {
      proxyContract: 'ERC1967Proxy', // default to "EIP173Proxy"
      proxyArgs: ['{implementation}', '{data}'],
      execute: {
        onUpgrade: {
          methodName: 'initialize',
          args: []
        }
      }
    }
  });

First time when this file is ran it deploys Implementation1, Proxy, Implementation2 & then executes upgradeTo.

Second time when this file runs it re-runs all the commands and will fail deploying Implementation1 as its initializer modifier protects it from being run again.

If it didnt have the initialize protection, it would have re-deployed Imlementation1, upgradeTo, and then Implementation2 and upgradeTo.

My Implentation Code is:

contract BoxUpgradable is Initializable, UUPSUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(address owner, uint _length, uint _width) public initializer {
        // solhint-disable-next-line security/no-inline-assembly
        assembly {
            sstore(
                0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103,
                owner
            )
        }
    }

    function _authorizeUpgrade(address newImplementation) override internal {}
}

contract Box2Upgradable is Initializable, UUPSUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize() public reinitializer(2) { }

    function _authorizeUpgrade(address newImplementation) override internal {}
}
RogerPodacter commented 1 year ago

Did you ever figure this one out? I'm having similar issues.

wighawag commented 1 year ago

you can set upgradeIndex (as parameter in the deploy function) to ensure each call is executed only once. forgot if this start at 1 or 0

RogerPodacter commented 1 year ago

Thank you for this! I did end up figuring it out as you describe. It starts at 0 I believe.