Hi, I am trying to upgrade my contract as per the OpenZeppelin tutorial (https://docs.openzeppelin.com/learn/upgrading-smart-contracts#initialization) The new contract initializes with an _admin variable, which the previous contract does not:
function initialize(address admin) public initializer {
_admin = admin;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() initializer {}
I want to upgrade to the new Box contract with _admin:
const BoxV2 = await ethers.getContractFactory("BoxV2");
await upgrades.upgradeProxy(box3Contract.address, BoxV2, {
constructorArgs: [owner.address],
});
So, I am trying to pass owner.address in the constructorArgs for initialization, but I keep getting the following error: Error: types/values length mismatch (count={"types":0,"values":1}, value={"types":[],"values":["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"]}, code=INVALID_ARGUMENT, version=abi/5.7.0) Anyone familiar with upgradeProxy ?
You are passing it as contracutor arguments but you need it in initialize() function
Sorry, how do you mean call?: string | { fn: string; args?: unknown[] }, under opts in upgradeProxy?
I have tried:
await upgrades.upgradeProxy(box3Contract.address, Box3V2, {
call: { fn: "initialize", args: [owner.address] },
});
However, it now results in the following error:
Error: types/values length mismatch (count={"types":1,"values":0}, value={"types":[{"name":"admin","type":"address","indexed":null,"components":null,"arrayLength":null,"arrayChildren":null,"baseType":"address","_isParamType":true}],"values":[]}, code=INVALID_ARGUMENT, version=abi/5.7.0)
Ok, the code above works.
Just need to remove the constructor function:
// constructor(address admin) initializer {
// _admin = admin;
// }
Now, I am unsure as to why the constructor is in the code in the docs 
Hi @Damien_Teo,
I'd like to clarify the concepts here on constructors vs. initializers:
- Generally an upgradeable implementation should not have a constructor. You should use an initializer to initialize your contract's logic.
- For deployProxy,
args and initializer are for the initializer's arguments and name, respectively.
- For upgradeProxy, if you need to call a function during the upgrade process, that is done with the
call?: string | { fn: string; args?: unknown[] }, option as you mentioned. This could be a function marked as reinitializer so that it can only be called once.
- A constructor with the below content is still recommended for disabling initialization of the implementation contract itself (at the implementation's address). The reasoning is described in this post.
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
Thanks Eric,
Very good explanation.
Think another issue was that I removed:
/// @custom:oz-upgrades-unsafe-allow constructor
This was causing an error stating that the contract was not update-safe