Overview of Proxy contracts on Ethereum

Overview of Proxy contracts on Ethereum

Prerequisites:

  • basic blockchain knowledge

  • smart contract knowledge

  • EVM knowledge

  • Solidity


Do we really want immutability?

We all know that blockchains has one of the feature called immutability means you can not modify the data on chain. That means once you deploy your contract you can't not change it without redeploying it with modification.

wdyt is that something we want in reality? we want our protocol to be immutable forever.

We all know that the requirements for any projects is never going to be limited to what we have decide when we deploy our contract. It will definitely going to be changed with the upgrades we will have in our projects/dapps/dAOs. And for that on every upgrade we have to redeploy the contract and have to update the address of it everywhere. that makes people lose trust on our project/dapp/DAO as they might not trust on new contract or several other reasons.

So that means we really don’t want immutability in our contracts. right??
but we can’t change the feature of the chain?
what’s the solution??

there are few solutions to this problem. following are some of them

  1. Creating multiple versions of a smart contract and migrating state (i.e., data) from the old contract to a new instance of the contract.

  2. Creating separate contracts to store business logic and state.

  3. Using proxy patterns to delegate function calls from an immutable proxy contract to a modifiable logic contract.

  4. Creating an immutable main contract that interfaces with and relies on flexible satellite contracts to execute specific functions.

Here comes the proxy contract in the picture. we will deep dive into the proxy patterns in this blog


What is proxy smart contracts?

So we saw that we can’t change the logic written in the smart contract but we definitely can change the code that’s executed when users interact with a smart contract.

Didn’t understand?
let me make it clear what if we have an proxy contract that has the address of our latest implemented contract?? we can change the contract address in that proxy contract without changing the contract address everywhere in our project. Users still will interact with the same contract address.

following image makes it more clear

There is one term called delegate call you can find more about it here
but in nutshell
There is a special type of call, Delegate Call which behaves differently in that it executes in the context of the calling contract (and msg.sender and msg.value do not change)

More details: https://eips.ethereum.org/EIPS/eip-897


What happens in proxy patterns/contracts?

The proxy pattern uses data separation to keep business logic and data in separate contracts. In a proxy pattern, the storage contract (called a proxy) calls the logic contract during code execution.

  1. Users interact with the proxy contract, which stores data, but doesn’t hold the business logic.

  2. The proxy contract stores the address of the logic contract and delegates all function calls to the logic contract (which holds the business logic) using the delegatecall function.

  3. After the call is forwarded to the logic contract, the returned data from the logic contract is retrieved and returned to the user.

The proxy contract is immutable by default, but new logic contracts with updated business logic can be created. Performing the upgrade is then a matter of changing the address of the logic contract referenced in the proxy contract.


Simple Proxy pattern:

following solidity code shows the basic proxy contract where it has implementation and admin address stored in storage and it has a function to upgrade the implementation contract address
only admin can call that function and upgrade the implementation contract address.

The remaining all calls will be delegated to the implementation contract as we have fallback function in the proxy contract.

contract AdminUpgradeableProxy {
    address implementation;
    address admin;

    fallback() external payable {
        // delegate here
    }

    function upgrade(address newImplementation) external {
        require(msg.sender == admin);
        implementation = newImplementation;
    }
}

The more secure version of AdminUpgradeableProxy is TransparentAdminUpgradeableProxy which is as below:

contract TransparentAdminUpgradeableProxy {
    address implementation;
    address admin;

    fallback() external payable {
        require(msg.sender != admin);
        // delegate here
    }

    function upgrade(address newImplementation) external {
        if (msg.sender != admin) fallback();
        implementation = newImplementation;
    }
}

But this is not efficient to implement upgradable smart contracts as when someone will call the function from implementation address it may overwrite the storage variables in implementation and admin slot in EVM. hence we will lost the implementation address and our contract will not work anymore or we may loose our funds as well.

It can be happened because our proxy contract is the storage contract where we will save state of our storage variables and delegate call will change the state of proxy contract when they call any function of implementation address.

To secure this problem we have different proxy patterns which we can use according to our needs.

Following are some of the patters:

  1. BeaconProxy

  2. IBeacon

  3. UpgradeableBeacon

  4. Clones

  5. Initializable

  6. UUPSUpgradeable

you can learn more about them in detail here


this was just the overview of the proxy contracts if you want to deep dive into it following are some of the resources that may help you:

Ref:

Thanks for reading