immutable in Solidity – Not Gone, Just Forgotten.
Tags: beginners, tutorial, solidity, web3
Published: 2022-06-20 19:07:53

It’s one of those articles which will acts as a beginner’s guide to best practices but be a short on. The immutable keyword in solidity is one of those things that many developers learn when going through the solidity docs or through a bootcamp.

Apparently, that’s the end of it. Because just like that the keyword is forgotten – just used to pass those certifications exams. Coming from any other programming language, things might seem a bit off in Solidity. I have felt off like this atleast twice – while learning Solidity and while learning Rust.

I mean if we are talking about Rust, why do we need to use the extra mut to make variables when we are already using var in the declaration? But that’s a topic for another discussion, right now let’s focus on the nuances of Solidity.

## `immutable`
In one line, the immutable keyword is used to define storage identifiers in a smart contract which would be initialized only during the contract deployment in the contract constructor or inline and there on out behave like a constant.

I am not really comfortable with the wording “immutable is used to define storage variables” because event though they can be thought of as a one-time variable data element, they are more of a cross between constants and variables (leaning more towards the former).
 
But what does this mean? Consider the problem – you have a variable in your smart contract which you know will only be changed once. This can happen when you are receiving values during contract deployment through the constructor. These values do not need to change during the remainder of the smart contract’s lifetime. You cannot use the keyword constant while defining that variable because then you will have to declare the value then and there. immutable keyword was brought in Solidity version 0.6.5 to remedy this situation.

## Small use case
One of the most common use cases of the keyword can be seen in my [previous articles](https://dev.to/abhikbanerjee99) where the usage of Chainlink VRF was explored via coding NFT smart contracts from scratch. As you may have noticed, these contracts had a VRF Coordinator address that was being passed on through the constructor towards the later articles.

The VRF Coordinator address is the address of the VRF Oracle contract on a specific chain and chances are that it won’t change ever. If so, then it might be just declared constant, right? But what happens when you want to generalize the contract? You could pass in the address via the constructor and have the address be immutable.

## Under the hood
Solidity compiles our contracts to bytecode which gets deployed on-chain. During the compilation process, if you have a `constant` storage variable (again, oxymoronic), then Solidity Compiler will replace all occurrences of that constant in the smart contract with its respective value. This happens because those values are available during compile time.

But with immutables, Solidity replaces those values in all references of the `immutable` in the smart contract during deployment – that is only when the constructor of the smart contract is invoked. If you have ever used Remix, you can try to check it out yourself. Just go the Compile tab and click on the option to copy the compiles bytecode as shown below.

![Remix, get Bytecode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rlb7meyvk8f91z12lq11.png)

Just paste in Notepad or something and then search for the value you gave to your constant during its declaration. You won’t find it. That’s because you need to search it in Hexadecimal. That’s a neat trick now? You will probably find it with a PUSH opcode. 

![Incredibles Meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yd552ci0if4sfz0zqr4l.gif)

But at this point, you won’t find the immutable value. After all, why would you? Have you even passed it yet? But if you deploy the contract on a testnet and then scroll down to the opcode section, you can find the hex encoded value in the opcode beside a PUSH operation (just like you found the value of the constant. This indicates prior to deployment the values were put into the code. You can go ahead and search for the hex encoded variable but you won’t find it. We demonstrate this with Code in the next section.

This should enforce the different storage types in solidity namely – `storage`, `memory` and `calldata` and how constants and immutables do not utilize any of those.


## Demonstrating with Code 
In this section we will go through a few very basic contracts which will show the functioning of the `immutable` type in Solidity and how it differs from `constant` and variables. The first code shown below is actually the contract demonstrating what we discussed towards the end of the last section. 

{% embed https://gist.github.com/abhik-99/dab5e9cbe701ec53a7a705f0e1f4781b %}

In the code above, you can see that there are 3 identifiers – `c1`, `i1` and `v1` for a constant, immutable and a variable respectively. Now there are 2 functions which you might notice are commented out. Those are for setting the constant and immutable. Obviously, you won’t be able to set the value of the constant `c1` after it has been defined inline before the constructor. You will get a warning like the one shown below before compilation.


![Cannot compile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0vro42mhfpksas9slxm.png)

You can try but the code won’t compile. Unsurprisingly, the same happens when you try to set the immutable outside the constructor. You can try it out yourself and would see a warning similar to the one below.

![Cannot Compile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3rce1cf3jj51hbhmvt6.png)

You can further compile the contract as in and search for `787632` in the compiled bytecode. `787632` is the hexadecimal for `7894578` and you will find it beside the **PUSH** op. Once you deploy the contract, you can try searching for the hexadecimal encoded immutable and you will find it as well. But you won’t find the hexadecimal encoded variable value in the deployed bytecode. I have deployed the contract on Polygon’s Mumbai testnet [here](https://mumbai.polygonscan.com/address/0xd594b3a7ff01269df811ecdd73c11b8b673a5895#code). The immutable `i1` has been initialized to `897568` while the variable has been initialized to `278563`. `DB220` and `44023` are the hex values for those respectively.

Interestingly, you will notice that the PUSH operation for the constant will look something like this:

> PUSH3 0x787632

While the PUSH operation for the immutable in our case will look something like this:

> PUSH32 0x00000000000000000000000000000000000000000000000000000000000db220

**What can we infer from here?** Head over to [evm.codes](https://www.evm.codes) and search for the `PUSH3` and `PUSH32` operations. Both of them are push operations but the former pushes 3 bytes while the latter 32. This shows that for our immutable `i1`, 32 bytes of contiguous storage was reserved which was filled in during contract deployment in the code itself. This shows the upper limit of 32 bytes for immutable type in Solidity. This should also explain why we cannot use immutable with arrays which would require an arbitrary amount of contiguous memory space in the code. It's not efficient and would make the code susceptible to high gas. Strings also cannot be used (though you can use bytes32). 

Moving on to our second contract, we will now focus on how the compiler can differentiate between constants and immutables and notify us in our functions. In the contract given below, you will find 4 functions. 2 functions each for the constant and immutable. When you put this contract in Remix, you will get 2 kinds of warnings.

{% embed https://gist.github.com/abhik-99/1717feea0d5d7bc137026e3415e186b6 %}

The first kind will tell you that you can turn the visibility of the functions `getConstant()` and `getConstantSum()` to `pure`. You might think that this is weird. You might have expected the warning to say `view` instead of `pure`. Afterall, you are still “technically” accessing a storage variable in these functions. But the thing is the value of the constant is replaced in all references of the constant in the contract code. This means that the functions `getConstant()` and `getConstantSum()` boil down to returning a integer value and a sum of two integer values. There is no need to refer to the `storage` class space for these. Since `pure` functions in solidity are the stateless functions (basically you can take those functions as is and stick them in any other contract but for a given set of inputs the output will always be the same regardless of the contract state), the above functions are essentially `pure` in nature. This is why you will get a warning like the one shown below.


![Convert to Pure Solidity Functions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q5dkub1y0fi1jaz8ne4.png)

This is different from the `getImmutable()` and `getImmutableSum()` functions. We know that all references of `i1` in the contract above will be replaced by its value during contract deployment. Those values are still not known to the compiler during the compilation stage. So, these functions are accessing a `storage` type space but not modifying – which is the trait of `view` function. Hence, a warning similar to the one below.


![Convert to view type solidity function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z3yd077hiqulfhpfpebo.png)

In the code below, you can see that it contains 3 contracts. Here, we will see the deployment and function invocation costs between `constant`, `immutable` and variables. These are shown with `Contract3`, `Contract5` and `Contract4` respectively. Each of these have a constructor which gets invoked during deployment and these contracts have a `getSum()` function which takes in a uint256 parameter. I haven’t deployed them to Mumbai testnet. The images shown after the code are from Remix deployments.

{% embed https://gist.github.com/abhik-99/c3610e713f3cc3a4680300f058827a29 %}

Hopefully, the code in the above gist is easy enough to understand. We shall now try to deploy them in the order `Contract3`, `Contract4` and `Contract5` and look at respective deployment and function invocation gas costs. First up the contract with only the constant.


![Solidity Contract Deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qprmy42sz6hu2w4hnsq1.png)


![getSum for constant](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppz0w32quaqrznnkhc81.png)


The pictures above show the deployment and `getSum()` function invocation for `Contract3` (with the constant only).

![Solidity Contract Deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6300s352khi4v3nuomn.png)


![getSum( with variable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nv4eji0wxw7yhb0mk6gw.png)

The pictures above show the deployment and `getSum()` function invocation for `Contract4` (with just a variable).


![Solidity Contract Deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vu6e4yyeqb9bdhba1ll4.png)


![getSum with immutable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hjd3bwe6hcwyrmhfzyvi.png)

The pictures above show the deployment and `getSum()` function invocation for `Contract5` (with immutable).

The difference is apparent. The gas costs with constant in deployment and `getSum()` invocation is least. The deployment and function invocation with variable is highest. The contract with immutable scores just in the middle. This shows that immutable is better than variable types when it comes to gas. It has the advantage of being initialized from the constructor as well as inline. This concludes what we needed to cover.

## Conclusion
In our three exercises in this section, we went through:
1.	Understanding the working of immutable type in Solidity.
2.	How immutables are seen by the compiler.
3.	The gas optimization benefits of immutable.

Frankly, this is almost everything to it. You should now have a clear understanding of the type. This article was different from my other articles. It was more fundamental in nature. Hopefully, you got something out of it.

If you have any other points to add that I may have missed, feel free to start a discussion below. You can reach out to me via Twitter or by mail. Until the next one – continue to build awesome stuff on Web3 and WAGMI!