Playing with OpenZeppelin proxy contracts
Tags: beginners, tutorial
Published: 2021-11-02 18:10:29

The code in this post is based on the popular OpenZeppelin contract implementation for a proxy pattern. The original code can be found [here](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v3.4.0/contracts/proxy). This post will help to understand a bit more about:
* proxy contract implementation
* how to deploy with a proxy pattern
* how to change implementation with proxy patterns

Before we begin there are some things that might help to grasp the subject matter better. [This](https://blog.openzeppelin.com/proxy-patterns/) article is a great starting point. Learning about the [layout of storage](https://medium.com/aigang-network/how-to-read-ethereum-contract-storage-44252c8af925) is also a good idea. Finally an understanding of [delegatecall](https://medium.com/coinmonks/delegatecall-calling-another-contract-function-in-solidity-b579f804178c) is essential to understand what is going on. Armed with an idea of these, we can go ahead and try to tackle the implementation itself.

![war](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ez4g6lg1jo0w7dp2pg6l.gif)
 

### Overview
We are going to deploy a simple contract, `Logic`, that adds two numbers. Then we are going to upgrade it to `UpgradedLogic`, an upgraded implementation that does the same thing, except that it uses the SafeMath library.

Click [here](https://remix.ethereum.org/?#gist=2849d9a09aa997989e838e3b269144e9&optimize=false&runs=200&evmVersion=null&version=soljson-v0.7.6+commit.7338295f.js) to open the contracts we'll be using in Remix.

The code that we will be using is based on [this gist](https://gist.github.com/td-bn/2849d9a09aa997989e838e3b269144e9). It is almost a carbon copy of the OpenZeppelin implementation but leaves out a few bits to makes things simpler.
It consists of the following contracts:
* Proxy, UpgradableProxy, TransparentUpgradableProxy and ProxyAdmin : These are slightly modified versions of OpenZeppelin's implementation of these contracts.
* Logic, UpgradedLogic: Contracts that hold the logic of our application
* Test: Contract that interacts with the proxy. Think of it like a user interacting with our application.
 
### Proxy and UpgradableProxy contracts

{% gist https://gist.github.com/td-bn/2849d9a09aa997989e838e3b269144e9 file=Proxy.sol %}

Overview:
* `fallback` function that calls `_fallback()` internally which in turn calls `_delegate()`. This is the code that delegates our call to the implementation. 
* Proxy is an abstract contract which does not have the implementation logic. That's where UpgradableProxy comes in.

{% gist https://gist.github.com/td-bn/2849d9a09aa997989e838e3b269144e9 file=UpgradableProxy.sol %}
Overview:
* constructor take in an address which is the contract that houses our logic
* implementation/logic contract address is stored at a random storage location called `IMPLEMENTATION_SLOT`. This is done to avoid clashes with default storage slots while managing the proxy. Understanding how storage works in Solidity will help drive this point home. 
* methods to read and write to `IMPLEMENTATION_SLOT`.
* public method to `getImplementation()`

{% gist https://gist.github.com/td-bn/2849d9a09aa997989e838e3b269144e9 file=Test.sol %}
This is a simple contract which takes the address of a proxy and makes a low level call to add two numbers. 


At this point we can deploy our basic proxy contract. 

Steps to Deploy:
1. Open the gist in Remix(Click [here](https://remix.ethereum.org/?#gist=2849d9a09aa997989e838e3b269144e9&optimize=false&runs=200&evmVersion=null&version=soljson-v0.7.6+commit.7338295f.js) to open these contracts and others we'll be using in Remix, if you haven't already)
2. Compile and deploy this `Logic.sol` contract. Test it by passing in two integers and verifying that the sum is correct in the Remix console.
![Deploy our implementation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9i4pakxaean7d67mf97.png)
3. To deploy the UpgradableProxy contract we need the address of our implementation/logic. Copy the address of the Logic contract deployed above and pass it into the constructor.
![deploy Upgradable proxy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/juxh6fo3a5u71su64szi.png)
4. Deploy the `Test.sol` contract and pass it the address of the UpgradedProxy we just deployed. 
![deploy Test contract](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/in83w41rz7pxm7pae20l.png)
5. Call the `test` function in the contract. It makes a low level call to the UpgradedProxy which in turn calls our Logic contract. If all goes well, you should see 0x2a as the output of the test call.
![verify result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9h834tdgid3c5pi11zq0.png)


### Wait.. how do we upgrade?
You might have noticed that the `_upgradeTo` function is internal, which means that we can't really call is from external contracts or accounts.

This is where the `TransparentUpgradableProxy` comes in which handles the admin logic for us. This contract exposes functions for upgrading implementations, as well as owners, who handle those changes. See the OpenZeppelin contract implementation of TransparentUpgradableProxy for more details. They have some great documentation. 

{% gist https://gist.github.com/td-bn/2849d9a09aa997989e838e3b269144e9 file=TransparentUpgradableProxy.sol %}
Overview:
* Admin slot to store the admin who can deploy and upgrade proxies. Again, to avoid collision with default storage.
* Note that it inherits from the UpgradableProxy contract which already has the proxy logic baked in.
* Methods for changing implementation and admin.

Deploying and testing:
1. Follow the steps above to deploy the Logic contract. 
2. Just like we deployed the Upgradable proxy in the step 3 above, we can deploy `TransparentUpgradableProxy` with the address of the implementation(Logic contract) and admin(use Remix account address)
3. Deploy the `Test.sol` contract and pass it the address of the TransparentUpgradableProxy we just deployed. 
4. Test that the proxy works. Also verify the event that is triggered.
5. Deploy UpgradedLogic and pass its address to the `upgradeTo` method of the TransparentUpgradableProxy(with the admin account, of course)
6. Use test contract again to verify the result. You should see a AddedSafely event in the console reflecting that the implementation was upgraded in the background, while the address you used to interact with remained the same.
![AddedSafely](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6ocg35endpmpjsmi4pi.png)


That's it for this post. I hope you have a better understanding of proxy contracts after reading and playing with the attached Remix gist.
A good exercise to understand these contracts might be to go through the OZ contract implementation and the related comments in the code. One could also learn about ProxyAdmin and the use cases that addresses.
 
Might post more about storage and its layout or OpenZeppelin's upgrade plugins in the future, both of which would build on top of this post.