The Upgrades plugin does not allow the execution of the constructor

I am aware of this warning: https://docs.openzeppelin.com/upgrades-plugins/proxies#the-constructor-caveat

But I still want to define immutable variables and initialize them through the constructor. The purpose is to reduce the gas when accessing variables

I also learned that when deploying an upgradeable contract using the Upgrades plugin, I can pass constructor arguments.

    function deployUUPSProxy(
        string memory contractName,
        bytes memory initializerData,
        Options memory opts
    ) internal returns (address) {
        address impl = deployImplementation(contractName, opts);


        return Core.deploy("ERC1967Proxy.sol:ERC1967Proxy", abi.encode(impl, initializerData), opts);
    }

In the Options struct:

    /*
     * Encoded constructor arguments for the implementation contract.
     * Note that these are different from initializer arguments, and will be used in the deployment of the implementation contract itself.
     * Can be used to initialize immutable variables.
     */
    bytes constructorData;

But when I tested it, I got an error.

:1234: Code to reproduce

        Options memory opts;
        opts.constructorData = abi.encode(addr01, addr02, addr03);

        address proxy = Upgrades.deployUUPSProxy(
            "MyContract.sol",
            abi.encodeCall(MyContract.initialize, ()),
            opts
        );
[FAIL: revert: Upgrade safety validation failed:
✘  src/MyContract.sol:MyContract

      src/MyContract.sol:43: Contract `MyContract` has a constructor
          Define an initializer instead
          https://zpl.in/upgrades/error-001

FAILED] setUp() (gas: 0)

Encountered a total of 1 failing tests, 0 tests succeeded

:laptop: Environment

"name": "@openzeppelin/foundry-upgrades",
"version": "0.4.0",

"name": "openzeppelin-solidity",
"description": "Secure Smart Contract library for Solidity",
"version": "5.2.0",

"name": "forge-std",
"version": "1.9.6",

I think you can manually disable the check using the option unsafeAllow: ['state-variable-immutable'] , or in Solidity >=0.8.2 placing the comment /// @custom:oz-upgrades-unsafe-allow state-variable-immutable before the variable declaration.

Yes, the comment should be added. But these are two different errors.

Initially, I did not add this comment to my variables, which caused two errors:

[FAIL: revert: Upgrade safety validation failed:
✘  src/MyContract.sol:MyContract

      src/MyContract.sol:43: Contract `MyContract` has a constructor
          Define an initializer instead
          https://zpl.in/upgrades/error-001
      
      src/MyContract.sol:27: Variable `addr01` is immutable and will be initialized on the implementation
          If by design, annotate with '@custom:oz-upgrades-unsafe-allow state-variable-immutable'
      Otherwise, consider a constant variable or use a mutable variable instead
          https://zpl.in/upgrades/error-005
      
      src/MyContract.sol:29: Variable `addr02` is immutable and will be initialized on the implementation
          If by design, annotate with '@custom:oz-upgrades-unsafe-allow state-variable-immutable'
      Otherwise, consider a constant variable or use a mutable variable instead
          https://zpl.in/upgrades/error-005
      
      src/MyContract.sol:31: Variable `addr03` is immutable and will be initialized on the implementation
          If by design, annotate with '@custom:oz-upgrades-unsafe-allow state-variable-immutable'
      Otherwise, consider a constant variable or use a mutable variable instead
          https://zpl.in/upgrades/error-005

FAILED] setUp() (gas: 0)

Then, I added the comment and the error about the immutable variable was solved, but the error about the constructor was still there.

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address public immutable addr01;    
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address public immutable addr02;
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address public immutable addr03;
[FAIL: revert: Upgrade safety validation failed:
✘  src/MyContract.sol:MyContract

      src/MyContract.sol:43: Contract `MyContract` has a constructor
          Define an initializer instead
          https://zpl.in/upgrades/error-001

FAILED] setUp() (gas: 0)

Oh, I think I found the answer.

It can turn off the error through --unsafeAllow in the Options structure.

1 Like

I see, how about adding comment for the constructor like followings:

/// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {}
2 Likes

It worked.
Thank you very much! :saluting_face:

1 Like