Immutable variables cannot be initialized

Got an error when assigning a value to an immutable variable in the initialize() function. Wondering if there is a workaround for immutable variables when using an Initializable contract.

TypeError: Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.

Could you please share your code?

// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

contract Testing is Initializable {
    
    uint256 private immutable MAX_SUPPLY;

    constructor() {}

    function initialize(uint256 a, uint256 b) public initializer {
        MAX_SUPPLY = a * b;
    }
}

As the error says, you cannot assign an immutable variable in any other place aside from the constructor or inlined in the contract body. Here's why (from the Solidity documentation):

The contract creation code generated by the compiler will modify the contract’s runtime code before it is returned by replacing all references to immutables by the values assigned to the them.

At deployment/creation time, the contract creation code will replace all references to the immutable var by its value (which comes either with the contract code if it's inlined or as an argument if it's in the constructor), but has no way to know what a and b for initialize will be.

That implies the workaround is not to use an immutable variable in an Initializable contract, correct?

1 Like

Unless you’re ok with an inlined fixed value, that’s correct :slight_smile:

That would defeat the purpose of using an immutable variable. One could just use constant. I think just being careful and never modifying the variable should work just fine. Thanks man.

Checking in to see if it's still not possible to achieve non-inlined immutable variables.

immutable variables would be significantly cheaper to reference than if they were loaded from storage. And some variables, like external proxy addresses, may never need to be updated, so it wouldn't make sense for these variables to not be immutable or constant.

Using constant would require a pre-processor like solpp to replace $(VARIABLE) at compile time, which is much more complex across multiple contracts, deployments, and networks, and thus less safe.

Below, contracts B and C keep a reference to A, but only C's reference is immutable. Why is it not possible to deploy C with a constructor that sets a before linking C to a proxy that can initialize it?

contract A {
    uint256 public x = 5;

    function getX() external view returns (uint256) {
        return x;
    }
}

contract B is Initializable {
    A public a;

    function initialize(A _a) public initializer {
        a = _a;
    }

    function getX() public view returns (uint256) {
        // This cold-loads `a` from storage.
        return a.getX();
    }
}

contract C is Initializable {
    A public immutable a;

    constructor(A _a) {
        a = _a;
    }

    function initialize() public initializer {}

    function getX() public view returns (uint256) {
        // This loads `a` from bytecode.
        return a.getX();
    }
}