-Proxies- New implementation contract how can get initial value?

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

:1234: 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;
    }
}

:computer: Environment

Hardhat

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;
    }
}

Or you have the following alternative: When is initialize() called? - #3 by frangio

1 Like

Thank you that is clear. One more question let say i have 5 events. Can i add more event on BoxV2 contract? What should i watch out for about events?

Sure, you can add as many Events as you want, since they're not stored in the contract's storage but rather in the blockchain's logs.

Hi, I have some other questions :slight_smile:
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.

Hi, Furkan!
It's important to understand that when speaking about upgradeable smart contracts, the initialize method acts like a constructor, so it must be called only once. I suggest you to read and understand more on this here: https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#the-constructor-caveat

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.

Thank you for you reply its really help. Can you also check this one please ?