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();
}
1 Like
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