12

There have been quite many arguments around how to achieve smart contract updates like Upgradeable smart contracts.
I feel Proxycontract with Delegate-call is currently the most famous one. But aside from that, there are also other patterns.

So, I list the summary of some of the most known patterns and compare each pattern.

1. Delegatecall-based proxies pattern
The most known one. also led by OpenZeppelin which distributes great tools and development kits. the proxy pattern splits a contract in two: one contract holding the logic and a proxy contract holding the data. In this pattern, the proxy contract calls the logic contract with delegatecall. Data returned by the logic contract will be stored in the proxy contract.

  • Pros: Most flexible that developer team can alter the contract completely. This enables unpredictable bug fix in the future and smooth update without any needs of user participation.
  • Cons: As mentioned in the trailofbits blog This pattern is most complicated that may cause serious damage to contracts. Implementing this requires a deep understanding of EVM. Thanks to OpenZeppelin, they check those complicated tasks. But, it's doubtful that a fully upgradable contract remains "Decentralized".

2. Data separation pattern

Also introduced in trailofbits blog. This pattern also separates data and logic. But, they don't use delegate-call, so an upgrade can be achieved by calling the new logic contract which stores data on the same contract.

  • Pros: Doesn't use complicated delegate-call function and doesn't require a deep understanding of EVM and can upgrade flexibly.
  • Cons: it still makes contract complicate and also it's doubtful that fully upgradable contract remains "Decentralized".

3. Registery pattern
This pattern was introduced in Consensys blog

  • Pros: Simple, easy to audit.
  • Cons: You will need to think carefully about how to deal with the contract data when you replace the contract

4. Updates through functions
This pattern is not standardized. Each project considers their upgradability before deployment and makes some of the contract(e,g, variables like the owner) upgradable through functions. I think Compound finance is using this pattern.

  • Pros: Simple, easy to audit, can reduce vulnerability by minimalizing upgradability.
  • Cons: Cannot deal with unexpected errors or bugs.


Thanks for reading so far. So, my questions are

  • If there is something wrong with my explanations, please give me feedback
  • Do you know any of active projects using one of those patterns?
  • Do you think of any other pros and cons, or other patterns?
5
  • 1
    Personally, I think a fully upgradable contract is no longer a Dapp. That can be seen as a centralized one and may be obliged to many regulations
    – kohshiba
    Sep 12, 2019 at 13:21
  • An upgradeable contract can be "decentralized", depending on the governance used e.g. using a DAO.
    – abcoathup
    Sep 13, 2019 at 0:50
  • Add to the reading list: Function clashing in Proxy contracts (does not impact OpenZeppelin SDK): forum.openzeppelin.com/t/…
    – abcoathup
    Sep 16, 2019 at 6:28
  • There is a new pattern that is given by the Diamond Standard. Check it out: github.com/ethereum/EIPs/issues/2535
    – Nick Mudge
    Mar 23, 2020 at 15:42
  • Currently one of the most notable proxy contract projects is USDC.
    – Jonathan
    Oct 23, 2022 at 12:26

2 Answers 2

5

OpenZeppelin uses the "unstructured storage" proxy pattern.
See the documentation for details: https://docs.openzeppelin.com/sdk/2.5/pattern

OpenZeppelin in the proxy patterns blog post (https://blog.openzeppelin.com/proxy-patterns/) explored three proxy pattern options:

  1. Inherited Storage
  2. Eternal Storage
  3. Unstructured Storage

An upgradeable contract can be "decentralized", depending on the governance used.
The upgrade mechanism for OpenZeppelin SDK upgradeable smart contracts can be controlled by any type of governance, be it a multi-signature wallet, a simple address or a complex DAO.
The documentation shows using a multi-signature wallet to manage contract upgrades: https://docs.openzeppelin.com/sdk/2.5/upgrades-governance

Example project
Coinbase and Circle’s USDC uses ZeppelinOS (now called OpenZeppelin SDK) proxy pattern: https://github.com/centrehq/centre-tokens


If you have questions on using OpenZeppelin you can ask in the Community Forum: https://forum.openzeppelin.com/

Disclosure: I am the Community Manager at OpenZeppelin

2

These 4 are really very well-know patterns.

(1) After analysing the Delegatecall-based approach, I gave up because of two main reasons: 1. You can only add new variables, not delete or change existing ones. So, if you have a struct defined and you want to change a type or add an existing field, or if you want to delete a variable, etc.... you cannot. 2. It makes the proxy low-level. Even if you use OpenZeppelin, this pattern will damage the transparency of the code as a whole. I did not understand what do you mean by "decentralized". I do not think this is the case.

(2) We are using the data-separation pattern. You can have a look at ERC930. It is great to keep the flexibility of updating data and schema. However, it makes life harder. The logic contract needs to know how to deal with the data storage. We created a mapping contract to help in that and an internal function to assembly the business object.

(3) I think the registry pattern without low-level is nice. We use it here. The option with low-level assembly is similar to the delegatecall one.

(4) This is a valid approach that can be used in specific situations.

I also recommend you to take a look at 1504. I like the way it introduces the upgrader contract. Upgrader is like a governance decision-making and a migration script in the DB world.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.