Contract Proxy Patterns
These are the major proxy patterns used in production-grade contracts
OpenZeppelin Transparent Upgradeable Proxies
EIP: None
In transparent proxies, the upgrade of the implementation is handled by the proxy. This leads to a larger contract size for the proxy. It is called "transparent", because the proxy is virtually indistinguishable to the end user from the logic contract (i.e the contract it proxies requests to). There are a few functions which only the admin can access, like upgradeTo(address implementation)
. None of the requests made by the admin are forwarded to the logic contract, thereby ensuring that proxy selector clashing does not occur. Note that in most cases, the admin of the proxy will be a contract ProxyAdmin
.
Implementation Examples:
If you're using hardhat, you will usually go with one of the following methods -
- Using hardhat-deploy. Take a look at the contracts and deploy subfolders
- Using @openzeppelin/hardhat-upgrades. This page has an accurate explanation and run-down on things that happen when you perform an upgrade
- An example you can look at - upgradeable-contracts-v2
If you're using truffle, you can use @openzeppelin/truffle-upgrades
OpenZeppelin UUPS
As mentioned in the EIP, UUPS stands for Universal Upgradeable Proxy Standard
It differs from transparent proxies, by leaving upgrade functionality to the implementation contracts. This allows for smaller proxy size, and cheaper deployment. It uses an ERC1967Proxy implementation for the proxy. By leaving the implementation to handle upgrades, it opens a backdoor of sorts, since the upgrades can be executed by an arbitrary address with selector clashing. That is why it is imperative to setup access-control for the admin functions, or by setting a flag that prevents upgrades.
Implementation Examples:
- This tutorial by OZ explains the difference between transparent and UUPS proxies, and shows you how to deploy using hardhat and truffle
Diamond
EIP: 2535
The diamond upgrade pattern employs a one-to-many proxy pattern. There can be multiple different implementations, each with their own functions that are proxied to. The best part about this pattern is that all current contracts are compliant to work as "Diamond Facets" (any function that is external to the proxy)
"Facets" can be added/removed/replaced easily, reducing complexity of contracts
Due to the structure of diamond contracts, it can be used to circumvent the 24kb limitation of contract size (since a diamond can have any amount of facets)
It has not been accepted yet, it is being deliberated here
Nick Mudge is the author of the pattern, and a lot of resources relating to it are on his blog.
Implementation Examples:
- OZ doesn't have a standard diamond library yet, but hardhat-deploy has an example here, which just uses Nick's reference implementation
Conclusion
Contract upgradeability is made easy by the folks over at OZ, which prevent you from reinventing the wheel for standard contracts!
Thanks for reading!