Proxy — Variable immutability

Eszymi
Coinmonks
Published in
4 min readSep 26, 2022

--

The blockchain technology is founded by few ideas: security, openness, equality, and immutability. Let’s focus on this last word. When we create any transaction, and it will be accepted inside the block, the information inside will never change. This transaction could be everything: creating new smart contract, interaction with other contract or sending Ether to someone etc.— it’s make no different. Every information saved to blockchain will be there forever. And thanks that every user of blockchain could sleep careless, without fear that someone change the rules of game, and they haven’t noticed it.

But this is a double-edged sword. If no one is able to change the content of the blockchain, then you also. Even if you want to change something inside your smart contract that you deployed and after that after you noticed a bug. Sorry, in your contract there will be always the bug unless you create new smart contract without this bug, and deploy it. But the wrong contract will still be in the blockchain. But it isn’t to be a problem, is it? The answer is: yes and no. When this contract is not relevant, e.g., you created it when you learned Solidity, it’s no problem. But when it is important, because this contract is a part of a big project, and by that bug someone could destroy the whole project, it is a massive issue, and we would like to solve it in a hurry. But how, when we can’t?

Solution number 1 — Migration

The one of solutions is creating a new bugless contract and deploy it. After that, inform every one, that all should use this new one. Easy, isn’t it? Not exactly, let’s look at the potential impact of this move. Imagine, the contract was very popular, and had a lot of user, who hard programming address of it inside their own contract. When we will deploy a new contract to replace the old one, all of them will have to change the address inside their contract. How do we change hard programming address? Of course, by write the new contract and deploy it. Do you see? It’s like an avalanche. On the beginning, it’s just a change of one contract, but it has potential to make a big insecurity impact. Why insecurity? Because, e.g., there could be someone who didn’t notice the change of the address.

Solution number 2 — Proxy

So, what is the other solution? Let me introduce to you, a proxy. In the most easy way, the proxy is a smart contract, that has an address of the second smart contract (call it Logic). The user when he would like to use any function from Logic, send transaction with information about name of function and sending value and so on to the Proxy’s address. In theory, Proxy doesn’t have a function with the same name as this in Logic, so transaction from the user go to the Proxy’s fallback. There is implemented a delegatecall to the Logic. Because Proxy use delegatecall, all change of states will be saved inside Proxy’s storage, not inside the Logic.

The idea of Proxy.

When someone will find a bug inside Logic, or someone would like to implement a new function, there is no easy way to change it. How? Just by replacing the address of Logic inside the Proxy. Thanks to that, the user will still use the same address (the Proxy address), but he will be able to use a new version of Logic.

The idea of Proxy is elegant and beautiful, but there are a few problems.

Problems with Proxy

No propriety use of delegatecall leads to gap of security, therefor it’s very important to have always the same set of the variable inside the Proxy and the Logic. The signature is only 4 bytes long, so it’s plausible the few functions could have the same signature. This problem is called Storage Collision. To avoid it, we use one of the three main way of organizing of the storage in Proxy: Inherited Storage, Eternal Storage and Unstructured Storage. I’ll write about all of them in my next posts.

The next difficulty with Proxy is no possibilities to use constructor to initialize the beginning value. Why? Because constructor set the value in the storage of the contract that has this constructor, not inside the Proxy, from where we read the value. So even if we set the value by the constructor, the proxy wouldn’t notice it. To avoid this, we could use initializer. It’s a function that could be call just once, and it contains all information we normally write inside constructor. Also, writing inside the Logic something like uint256 public InitValue = 42; won’t initialize the value, because setting the value like that is change by compiler to setting the value by constructor. Therefor all this kind operation we could put to the initializer.

How I mentioned, the Proxy use fallback method, where is implemented delegatecall, which use function from Logic. We know EVM use signature to choose called function. What if the Proxy and the Logic have a function with the same name. How EVM will know which of them it should call? This problem is called proxy selector clashing.

At this moment there are 3 of the most popular pattern of proxy: The Transparent Proxy Pattern, Universal Upgradeable Proxy Standard and Diamond Pattern. I’ll show them in my next posts. All of them are created to avoid the proxy selector clashing.

New to trading? Try crypto trading bots or copy trading

--

--