Hi everyone,
I have a simple transparent proxies example. I want to upgrade box contract to boxV2. On boxV2 contract i want to declare new variable and give it a initial value. How can i do that?
I tought on BoxV2 should call initialize function when it deployed but it doesnt? So i came up a setup function but it has be revoke. is there any other way?
Is this causing becase of the box contract constructor? It has _disableInitializers function.
Thank you
Code to reproduce
Box Contract:
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract Box is Initializable {
uint256 public value;
function initialize(uint256 _value) public initializer {
value = _value;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
}
BoxV2 contract:
// contracts/BoxV2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract BoxV2 is Initializable {
uint256 private value;
uint256 public x;
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
function initialize() public initializer {
x = 500;
}
function setup() public {
x = 700;
}
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
// Increments the stored value by 1
function increment() public {
value = value + 1;
emit ValueChanged(value);
}
function setX(uint256 newValue) public {
x = newValue;
}
function getValue() public view returns (uint256) {
return x;
}
}
The initialize function will be called only on time, so you can't just update it's parameters and reuse it.
What I'd do in your case is:
keep the same initialize method definition
add your new variable at the end of the storage
add a new method setX to update the value and a getter - getX
upgrade to BoxV2
call the setX method to update the variable value
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract Box is Initializable {
uint256 public value;
uint256 public x;
function initialize(uint256 _value) public initializer {
value = _value;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
function setX(uint256 newValue) public {
x = newValue;
}
function getX() public view returns (uint256) {
return x;
}
}
Hi, I have some other questions
1- The initialize function on BoxV2 contract will be ever invoke?
2- On BoxV2, can i invoke initialize function after deployment? I guest not but i want to be sure.
So, to answer your questions, the initialize method will be executed only once, when deploying BoxV1 and you'll not be able to call it manually after deploying BoxV2 because it's protected by the initializer modifier which checks the _initialized variable to indicate whether or not the proxy contract has been initialized.
To conclude, I suggest you following the same path described above, where you have to manually execute a different method to set the initial value for your new variables.