/ EIPS / eip-1822.md
eip-1822.md
  1  ---
  2  eip: 1822
  3  title: Universal Upgradeable Proxy Standard (UUPS)
  4  author: Gabriel Barros <gabriel@terminal.co>, Patrick Gallagher <blockchainbuddha@gmail.com>
  5  discussions-to: https://ethereum-magicians.org/t/eip-1822-universal-upgradeable-proxy-standard-uups
  6  status: Draft
  7  type: Standards Track
  8  category: ERC
  9  created: 2019-03-04
 10  ---
 11  
 12  ## Table of contents
 13  
 14  <!-- TOC -->
 15  
 16  - [Table of contents](#table-of-contents)
 17  - [Simple Summary](#simple-summary)
 18  - [Abstract](#abstract)
 19  - [Motivation](#motivation)
 20  - [Terminology](#terminology)
 21  - [Specification](#specification)
 22    - [Proxy Contract](#proxy-contract)
 23      - [Functions](#functions)
 24        - [`fallback`](#fallback)
 25      - [`constructor`](#constructor)
 26    - [Proxiable Contract](#proxiable-contract)
 27      - [Functions](#functions-1)
 28        - [`proxiable`](#proxiable)
 29        - [`updateCodeAddress`](#updatecodeaddress)
 30  - [Pitfalls when using a proxy](#pitfalls-when-using-a-proxy)
 31    - [Separating Variables from Logic](#separating-variables-from-logic)
 32    - [Restricting dangerous functions](#restricting-dangerous-functions)
 33  - [Examples](#examples)
 34    - [Owned](#owned)
 35    - [ERC-20 Token](#erc-20-token)
 36      - [Proxy Contract](#proxy-contract-1)
 37      - [Token Logic Contract](#token-logic-contract)
 38  - [References](#references)
 39  - [Copyright](#copyright)
 40      <!-- /TOC -->
 41  
 42  ## Simple Summary
 43  
 44  Standard upgradeable proxy contract.
 45  
 46  ## Abstract
 47  
 48  The following describes a standard for proxy contracts which is universally compatible with all contracts, and does not create incompatibility between the proxy and business-logic contracts. This is achieved by utilizing a unique storage position in the proxy contract to store the Logic Contract's address. A compatibility check ensures successful upgrades. Upgrading can be performed unlimited times, or as determined by custom logic. In addition, a method for selecting from multiple constructors is provided, which does not inhibit the ability to verify bytecode.
 49  
 50  ## Motivation
 51  
 52  - Improve upon existing proxy implementations to improve developer experience for deploying and maintaining Proxy and Logic Contracts.
 53  
 54  - Standardize and improve the methods for verifying the bytecode used by the Proxy Contract.
 55  
 56  ## Terminology
 57  
 58  - `delegatecall()` - Function in contract **A** which allows an external contract **B** (delegating) to modify **A**'s storage (see diagram below, [Solidity docs](https://solidity.readthedocs.io/en/v0.5.3/introduction-to-smart-contracts.html#delegatecall-callcode-and-libraries))
 59  - **Proxy Contract** - The contract **A** which stores data, but uses the logic of external contract **B** by way of `delegatecall()`.
 60  - **Logic Contract** - The contract **B** which contains the logic used by Proxy Contract **A**
 61  - **Proxiable Contract** - Inherited in Logic Contract **B** to provide the upgrade functionality
 62  
 63  <p align="center"><img src="../assets/eip-1822/proxy-diagram.png" alt="diagram" width="600"/></p>
 64  
 65  ## Specification
 66  
 67  The Proxy Contract proposed here should be deployed _as is_, and used as a drop-in replacement for any existing methods of lifecycle management of contracts. In addition to the Proxy Contract, we propose the Proxiable Contract interface/base which establishes a pattern for the upgrade which does not interfere with existing business rules. The logic for allowing upgrades can be implemented as needed.
 68  
 69  ### Proxy Contract
 70  
 71  #### Functions
 72  
 73  ##### `fallback`
 74  
 75  The proposed fallback function follows the common pattern seen in other Proxy Contract implementations such as [Zeppelin][1] or [Gnosis][2].
 76  
 77  However, rather than forcing use of a variable, the address of the Logic Contract is stored at the defined storage position `keccak256("PROXIABLE")`. This eliminates the possibility of collision between variables in the Proxy and Logic Contracts, thus providing "universal" compatibility with any Logic Contract.
 78  
 79  ```javascript
 80  function() external payable {
 81      assembly { // solium-disable-line
 82          let contractLogic := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7)
 83          calldatacopy(0x0, 0x0, calldatasize)
 84          let success := delegatecall(sub(gas, 10000), contractLogic, 0x0, calldatasize, 0, 0)
 85          let retSz := returndatasize
 86          returndatacopy(0, 0, retSz)
 87          switch success
 88          case 0 {
 89              revert(0, retSz)
 90          }
 91          default {
 92              return(0, retSz)
 93          }
 94      }
 95  }
 96  ```
 97  
 98  #### `constructor`
 99  
100  The proposed constructor accepts any number of arguments of any type, and thus is compatible with any Logic Contract constructor function.
101  
102  In addition, the arbitrary nature of the Proxy Contract's constructor provides the ability to select from one or more constructor functions available in the Logic Contract source code (e.g., `constructor1`, `constructor2`, ... etc. ). Note that if multiple constructors are included in the Logic Contract, a check should be included to prohibit calling a constructor again post-initialization.
103  
104  It's worth noting that the added functionality of supporting multiple constructors does not inhibit verification of the Proxy Contract's bytecode, since the initialization tx call data (input) can be decoded by first using the Proxy Contract ABI, and then using the Logic Contract ABI.
105  
106  The contract below shows the proposed implementation of the Proxy Contract.
107  
108  ```javascript
109  contract Proxy {
110      // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"
111      constructor(bytes memory constructData, address contractLogic) public {
112          // save the code address
113          assembly { // solium-disable-line
114              sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, contractLogic)
115          }
116          (bool success, bytes memory _ ) = contractLogic.delegatecall(constructData); // solium-disable-line
117          require(success, "Construction failed");
118      }
119  
120      function() external payable {
121          assembly { // solium-disable-line
122              let contractLogic := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7)
123              calldatacopy(0x0, 0x0, calldatasize)
124              let success := delegatecall(sub(gas, 10000), contractLogic, 0x0, calldatasize, 0, 0)
125              let retSz := returndatasize
126              returndatacopy(0, 0, retSz)
127              switch success
128              case 0 {
129                  revert(0, retSz)
130              }
131              default {
132                  return(0, retSz)
133              }
134          }
135      }
136  }
137  ```
138  
139  ### Proxiable Contract
140  
141  The Proxiable Contract is included in the Logic Contract, and provides the functions needed to perform an upgrade. The compatibility check `proxiable` prevents irreparable updates during an upgrade.
142  
143  > :warning: Warning: `updateCodeAddress` and `proxiable` must be present in the Logic Contract. Failure to include these may prevent upgrades, and could allow the Proxy Contract to become entirely unusable. See below [Restricting dangerous functions](#restricting-dangerous-functions)
144  
145  #### Functions
146  
147  ##### `proxiable`
148  
149  Compatibility check to ensure the new Logic Contract implements the Universal Upgradeable Proxy Standard. Note that in order to support future implementations, the `bytes32` comparison could be changed e.g., `keccak256("PROXIABLE-ERC1822-v1")`.
150  
151  ##### `updateCodeAddress`
152  
153  Stores the Logic Contract's address at storage `keccak256("PROXIABLE")` in the Proxy Contract.
154  
155  The contract below shows the proposed implementation of the Proxiable Contract.
156  
157  ```javascript
158  contract Proxiable {
159      // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"
160  
161      function updateCodeAddress(address newAddress) internal {
162          require(
163              bytes32(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) == Proxiable(newAddress).proxiableUUID(),
164              "Not compatible"
165          );
166          assembly { // solium-disable-line
167              sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, newAddress)
168          }
169      }
170      function proxiableUUID() public pure returns (bytes32) {
171          return 0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7;
172      }
173  }
174  ```
175  
176  ## Pitfalls when using a proxy
177  
178  The following common best practices should be employed for all Logic Contracts when using a proxy contract.
179  
180  ### Separating Variables from Logic
181  
182  Careful consideration should be made when designing a new Logic Contract to prevent incompatibility with the existing storage of the Proxy Contract after an upgrade. Specifically, the order in which variables are instantiated in the new contract should not be modified, and any new variables should be added after all existing variables from the previous Logic Contract
183  
184  To facilitate this practice, we recommend utilizing a single "base" contract which holds all variables, and which is inherited in subsequent logic contract(s). This practice greatly reduces the chances of accidentally reordering variables or overwriting them in storage.
185  
186  ### Restricting dangerous functions
187  
188  The compatibility check in the Proxiable Contract is a safety mechanism to prevent upgrading to a Logic Contract which does not implement the Universal Upgradeable Proxy Standard. However, as occurred in the parity wallet hack, it is still possible to perform irreparable damage to the Logic Contract itself.
189  
190  In order to prevent damage to the Logic Contract, we recommend restricting permissions for any potentially damaging functions to `onlyOwner`, and giving away ownership of the Logic Contract immediately upon deployment to a null address (e.g., address(1)). Potentially damaging functions include native functions such as `SELFDESTRUCT`, as well functions whose code may originate externally such as `CALLCODE`, and `delegatecall()`. In the [ERC-20 Token](#erc-20-token) example below, a `LibraryLock` contract is used to prevent destruction of the logic contract.
191  
192  ## Examples
193  
194  ### Owned
195  
196  In this example, we show the standard ownership example, and restrict the `updateCodeAddress` to only the owner.
197  
198  ```javascript
199  contract Owned is Proxiable {
200      // ensures no one can manipulate this contract once it is deployed
201      address public owner = address(1);
202  
203      function constructor1() public{
204          // ensures this can be called only once per *proxy* contract deployed
205          require(owner == address(0));
206          owner = msg.sender;
207      }
208  
209      function updateCode(address newCode) onlyOwner public {
210          updateCodeAddress(newCode);
211      }
212  
213      modifier onlyOwner() {
214          require(msg.sender == owner, "Only owner is allowed to perform this action");
215          _;
216      }
217  }
218  ```
219  
220  ### ERC-20 Token
221  
222  #### Proxy Contract
223  
224  ```javascript
225  pragma solidity ^0.5.1;
226  
227  contract Proxy {
228      // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"
229      constructor(bytes memory constructData, address contractLogic) public {
230          // save the code address
231          assembly { // solium-disable-line
232              sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, contractLogic)
233          }
234          (bool success, bytes memory _ ) = contractLogic.delegatecall(constructData); // solium-disable-line
235          require(success, "Construction failed");
236      }
237  
238      function() external payable {
239          assembly { // solium-disable-line
240              let contractLogic := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7)
241              calldatacopy(0x0, 0x0, calldatasize)
242              let success := delegatecall(sub(gas, 10000), contractLogic, 0x0, calldatasize, 0, 0)
243              let retSz := returndatasize
244              returndatacopy(0, 0, retSz)
245              switch success
246              case 0 {
247                  revert(0, retSz)
248              }
249              default {
250                  return(0, retSz)
251              }
252          }
253      }
254  }
255  ```
256  
257  #### Token Logic Contract
258  
259  ``` javascript
260  
261  contract Proxiable {
262      // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"
263  
264      function updateCodeAddress(address newAddress) internal {
265          require(
266              bytes32(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) == Proxiable(newAddress).proxiableUUID(),
267              "Not compatible"
268          );
269          assembly { // solium-disable-line
270              sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, newAddress)
271          }
272      }
273      function proxiableUUID() public pure returns (bytes32) {
274          return 0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7;
275      }
276  }
277  
278  
279  contract Owned {
280  
281      address owner;
282  
283      function setOwner(address _owner) internal {
284          owner = _owner;
285      }
286      modifier onlyOwner() {
287          require(msg.sender == owner, "Only owner is allowed to perform this action");
288          _;
289      }
290  }
291  
292  contract LibraryLockDataLayout {
293    bool public initialized = false;
294  }
295  
296  contract LibraryLock is LibraryLockDataLayout {
297      // Ensures no one can manipulate the Logic Contract once it is deployed.
298      // PARITY WALLET HACK PREVENTION
299  
300      modifier delegatedOnly() {
301          require(initialized == true, "The library is locked. No direct 'call' is allowed");
302          _;
303      }
304      function initialize() internal {
305          initialized = true;
306      }
307  }
308  
309  contact ERC20DataLayout is LibraryLockDataLayout {
310    uint256 public totalSupply;
311    mapping(address=>uint256) public tokens;
312  }
313  
314  contract ERC20 {
315      //  ...
316      function transfer(address to, uint256 amount) public {
317          require(tokens[msg.sender] >= amount, "Not enough funds for transfer");
318          tokens[to] += amount;
319          tokens[msg.sender] -= amount;
320      }
321  }
322  
323  contract MyToken is ERC20DataLayout, ERC20, Owned, Proxiable, LibraryLock {
324  
325      function constructor1(uint256 _initialSupply) public {
326          totalSupply = _initialSupply;
327          tokens[msg.sender] = _initialSupply;
328          initialize();
329          setOwner(msg.sender);
330      }
331      function updateCode(address newCode) public onlyOwner delegatedOnly  {
332          updateCodeAddress(newCode);
333      }
334      function transfer(address to, uint256 amount) public delegatedOnly {
335          ERC20.transfer(to, amount);
336      }
337  }
338  ```
339  
340  ## References
341  
342  - ["Escape-hatch" proxy Medium Post](https://medium.com/terminaldotco/escape-hatch-proxy-efb681de108d)
343  
344  ## Copyright
345  
346  Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
347  
348  [1]: https://github.com/maraoz/solidity-proxy/blob/master/contracts/Dispatcher.sol
349  [2]: https://blog.gnosis.pm/solidity-delegateproxy-contracts-e09957d0f201