Initialize an upgradeable contract instance

Hi everyone,

I'm trying to set private upgradeable values from parameters provided to the initialize function of my upgradeable contract.

Contract creation works well and calls the initialize function as expected, but when I try to update the contract with a new instance, I cannot figure how I can provide different values to the initialize function. I'm not even sure I can expect the initialize function to be called upon upgrade since it holds the initializer modifier.

I'm using Hardhat to test/deploy my contract.

:1234: Code to reproduce

Provided the following simple example:

// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract MyContract is Initializable {
    uint256 private _price;

    function initialize(uint256 price) public initializer {
        _price = price;
    }
}

I'm deploying the contract with the following hardhat commands:

await upgrades.deployProxy(await ethers.getContractFactory("MyContract"), [
    ethers.utils.parseEther("0.02")
]);

Which works for the first implementation of my contract, setting the private _price attribute to 0.02eth.

But then I cannot figure out how I can deploy a new version of my contract with a different price?

I tried doing:

await upgrades.upgradeProxy(
  myOldContractInstance,
  await ethers.getContractFactory("MyContract"),
  {
    call: {
      fn: "initialize",
      args: [
        ethers.utils.parseEther("0.01")
      ],
    },
  }
)

But the initialize method does not seem to be callable on the new contract implementation.

As far as I understand, the "initialized" flag of my contract persists through proxy updates?

How should I proceed to update the price? My two ideas so far:

  • add another function setting the price, something like setPrice(uint256) public onlyOwner {...} and call it during proxy update -> should work but I feel I'm twisting OZ upgradeability logic
  • add the same function add call it without deploying anything as a regular function call -> works in a simple case but can be tedious if I need to upgrade several parameters

:computer: Environment

I'm using Hardhat as my basic tooling for contract deployment and testing.

Hello @Bernardstanislas

The initializer modifier in prevent the initialize function to ever be re-executed. This includes re-calling it during an upgrade. This is a limitation we will have to address.

IMO, the best setPrice() option is a good one. It could either be:

  • protected by onlyOwner
  • open to anyone, include a initializer-like flag to prevent re-execution AND be executed during the upgrade call.

Hi @Amxx ,

Thank you for your reply :slight_smile: I'll go for the setPrice() option then.

Thank your again!