Using Multiple Initializers in Upgradable Smart Contract

For Upgradable Smart contract following pattern as specified here:

"Constructor"/Initializer has form:

function initialize() public initializer {
       .......
       .......
       .......
    }

Now suppose I have a set of initial value field declarations and I set them in a separate initializer - say:

function initializeInitialValues() public initializer {
       .......
       .......
       .......
    }

The code below does not pick up the initializers

let MyContract;
let mycontract;

describe("MyContract", function() {

  beforeEach(async function () {
     MyContract= await ethers.getContractFactory("MyContract");
     mycontract= await upgrades.deployProxy(MyContract, {initializer: 'initialize()', initializer: 'initializeInitialValues()'});
  });
       .......
       .......
       .......

});

Using only await upgrades.deployProxy(MyContract, {initializer: 'initialize()'} and putting all values in there works. i.e.

function initialize() public initializer {
       constructor values
              &
       initial value field declarations
    }

When deploying/testing the smart contract how do I specify multiple initializers?

Especially as a means to logically separate unrelated components in the upgradable smart contract. i.e. constructor values vs initial value field declarations

the whole point of using initializers is to avoid using the constructor. You can split your initialize function into multiple initialize functions to encapsulate the fields better.

Hi @STYJ I think you are misunderstanding the question. I know that you cannot use constructors in upgradable smart contracts - what I am asking is the syntax to list multiple initializers.

So if I have two initialize functions i.e. 'initialize() and 'initializeInitialValues()'

How do you specify both initializers in the deployment script?

The syntax:
```` mycontract= await upgrades.deployProxy(MyContract, {initializer: 'initialize()', initializer: 'initializeInitialValues()'});```

Does not detect both initializers.

Ah my bad, I must have misunderstood. This is less of an openzeppelin issue and more of an ethers issue - how do you call a function on ethers.

MyContract= await ethers.getContractFactory("MyContract"); only creates an instance of the factory, it doesn't actually deploy your contract. After creating a contract factory, you need to call factory.deploy() to deploy an instance of the contract.

Once the contract is deployed, you can call the initialize() and initializeInitialValues() functions.

Take a look at this, it might be able to give you some ideas on how to start.

@STYJ I am using the upgrades plugin and deploying with await upgrades.deployProxy - also given that initializers are acting as constructors you should not call initialize() or initializeInitialValues() but rather call functions that access those values. Even if you were to call these it will throw at Initializable.sol

With respect to the question check this gist I created quickly to elaborate: https://gist.github.com/Leon-Africa/7b98c84956533eaaea27682f700fb23f

deployProxy has the initializer option that you're correctly using, but it only supports passing in a single function. At the smart contract level, only a single function is supported.

You can split your logic across different functions if that helps you organize the code, but in the end all of these should be invoked by a single global initializer.

3 Likes

@frangio thanks for the clarity :+1: