From 5db4b6843954e5bd98223ffa6e10f581e0db6b62 Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Wed, 1 Jun 2022 11:20:04 -0400 Subject: [PATCH 01/11] Finalize diamond standard --- EIPS/eip-2535.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index df287982e29a8..10e7b3d264930 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -3,8 +3,7 @@ eip: 2535 title: Diamonds, Multi-Facet Proxy author: Nick Mudge (@mudgen) discussions-to: https://github.com/ethereum/EIPs/issues/2535 -status: Last Call -last-call-deadline: 2020-08-15 +status: Final type: Standards Track category: ERC created: 2020-02-22 From 3b28ab5dbc4745a5c34810f9399554e698c70d7d Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Sat, 4 Jun 2022 09:34:57 -0400 Subject: [PATCH 02/11] Commit changes --- EIPS/eip-2535.md | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index 10e7b3d264930..a4a30c7f69a28 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -3,7 +3,8 @@ eip: 2535 title: Diamonds, Multi-Facet Proxy author: Nick Mudge (@mudgen) discussions-to: https://github.com/ethereum/EIPs/issues/2535 -status: Final +status: Review +description: Standard for creating modular smart contract systems that can be extended after deployment type: Standards Track category: ERC created: 2020-02-22 @@ -11,20 +12,6 @@ created: 2020-02-22 -## Simple Summary - -Standard for creating modular smart contract systems that can be extended after deployment. - -Enables people to write smart contracts with virtually no size limit. - -Diamonds can be upgraded without having to redeploy existing functionality. Parts of a diamond can be added/replaced/removed while leaving other parts alone. - -Standardizes contract interfaces and implementation details of diamonds, enabling software integration and interoperability. - -A diamond is a contract that implements the Specification in this standard. - -Diamond analogy helps conceptualize development. - ## Motivation There are a number of different reasons to use diamonds. Here are some of them: From 09bc41e56bf760e95173542c2020f888adaf304e Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Wed, 8 Jun 2022 11:05:43 -0400 Subject: [PATCH 03/11] Make a few changes --- EIPS/eip-2535.md | 88 ++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index a4a30c7f69a28..4a3f95c85092a 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -4,22 +4,36 @@ title: Diamonds, Multi-Facet Proxy author: Nick Mudge (@mudgen) discussions-to: https://github.com/ethereum/EIPs/issues/2535 status: Review -description: Standard for creating modular smart contract systems that can be extended after deployment +description: A standard for creating modular smart contract systems that can be extended after deployment type: Standards Track category: ERC created: 2020-02-22 +requires: 1538, 2535 --- + +## Abstract +This EIP proposes a standard for creating modular smart contract systems that can be extended after deployment ("Diamonds"). This enables smart contracts to effectively have no size limit, and to be upgraded without having to redeploy existing functionality. A Diamond's "Facets" can be added, replaced, or removed without having to worry about unintended side-effects, providing a layer of abstraction that aids development. + ## Motivation There are a number of different reasons to use diamonds. Here are some of them: -1. **A single address for unlimited contract functionality.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. -1. **Your contract exceeds the 24KB maximum contract size.** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. -1. **A diamond provides a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a gas-efficient way. -1. **A diamond provides a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgradeable or immutable. It is also possible to make an upgradeable diamond and then at a later time remove its upgrade capability. +1. **Diamonds can retain addresses through upgrades.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. +1. **Diamonds can exceed the maximum contract size (Currently 24KB).** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. +1. **Diamonds provide a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a gas-efficient way. +1. **Diamonds provide a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgradeable or immutable. It is also possible to make an upgradeable diamond and then at a later time remove its upgrade capability. + +## Specification +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +## What is a Diamond? + +A diamond is a contract with external functions that are supplied by contracts called **facets**. + +Facets are separate, independent contracts that can share internal functions, libraries and state variables. ### Diamonds Support Transparency @@ -41,17 +55,11 @@ The loupe functions can be used for many things including: Diamonds support another form of transparency which is a historical record of all upgrades on a diamond. This is done with the DiamondCut event which is used to record all functions that are added, replaced or removed on a diamond. -This standard is an improvement of [EIP-1538 Transparent Contract Standard](https://eips.ethereum.org/EIPS/eip-1538). The same motivations of that standard apply to this standard. +This standard is an improvement of [EIP-1538 Transparent Contract Standard](./eip-1538.md). The same motivations of that standard apply to this standard. -See the [Learning & References section](https://eips.ethereum.org/EIPS/eip-2535#learning--references) for additional information and uses of diamonds. +See the [Learning & References section](./eip-2535.md#learning--references) for additional information and uses of diamonds. -## What is a Diamond? - -A diamond is a contract with external functions that are supplied by contracts called **facets**. - -Facets are separate, independent contracts that can share internal functions, libraries and state variables. - -## How a Diamond Works +### How a Diamond Works A diamond stores within it a mapping of function selector to facet address, for example `selectorToFacet`. @@ -90,7 +98,7 @@ An event is emitted any time external functions are added, replaced or removed t A diamond has four standard external functions that can be called to show what facets and functions it currently has. -## Upgrades and Immutability +### Upgrades and Immutability A diamond can be immutable from inception by not adding any external functions that can add/replace/remove functions. [There are number of reasons to do this.](#different-kinds-of-diamonds) @@ -98,7 +106,7 @@ A diamond that is mutable can be made immutable by removing such functions. A diamond can be upgraded if it has a diamondCut external function or other function(s) that can add/replace/remove functions. -## Organizing State Variables in Diamonds +### Organizing State Variables in Diamonds A state variable or storage layout organizational pattern is needed because Solidity's builtin storage layout system doesn't support proxy contracts or diamonds. @@ -106,7 +114,7 @@ Described below are Diamond Storage and AppStorage which are two state variable Another successful pattern has been to write a storage contract that contains all state variables and is inherited by all facets. -## Facets, State Variables and Diamond Storage +### Facets, State Variables and Diamond Storage A facet defines external functions and can define or use internal functions, libraries, and state variables. @@ -158,7 +166,7 @@ By using Diamond Storage facets can declare their own state variables that do no By using Diamond Storage facets can be developed independently, without connection or concern for other facets. -## AppStorage +### AppStorage A specialized version of Diamond Storage is AppStorage. This pattern is used to more conveniently and easily share state variables between facets. @@ -187,13 +195,13 @@ The above example accesses the `s.firstVar` state variable and stores a computat Read this article to learn more about AppStorage: [AppStorage Pattern for State Variables in Solidity](https://dev.to/mudgen/appstorage-pattern-for-state-variables-in-solidity-3lki). -## Diamonds Can Use Other Contract Storage Strategies +### Diamonds Can Use Other Contract Storage Strategies Diamonds and facets don't have to use Diamond Storage or AppStorage. They can use or mix with other contract storage strategies such as contract inheritance. Diamonds only need to implement the [Specification section](#specification) of this standard. -## Facets Can Share State and Functionality +### Facets Can Share State and Functionality Facets can share state variables by using the same structs at the same storage positions. @@ -201,7 +209,7 @@ Facets can share internal functions and libraries by inheriting the same contrac In these ways facets are separate, independent units but can share state and functionality. -## Facets are Reusable and Composable +### Facets are Reusable and Composable A deployed facet can be used by any number of diamonds. @@ -213,15 +221,15 @@ The ability to use the same deployed facets for many diamonds reduces deployment A limitation is that two external functions with the same function signature can't be added to a diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature. -## Diagrams +### Diagrams -### Diamond Structure +#### Diamond Structure This diagram shows the structure of a diamond: -### Diamond Storage +#### Diamond Storage The diagram below shows facets with their own data and data shared between them. @@ -237,7 +245,7 @@ In this diagram -### Deployed Facets Can Be Reused +#### Deployed Facets Can Be Reused A deployed facet can be used by any number of diamonds. @@ -250,7 +258,7 @@ The diagram below shows two diamonds using the same two facets. -## Some Diamond Benefits +#### Some Diamond Benefits 1. A stable contract address that provides needed functionality. 1. A single address with the functionality of multiple contracts (facets) that are independent from each other but can share internal functions, libraries and state variables. @@ -276,7 +284,7 @@ The diagram below shows two diamonds using the same two facets. 1. Save gas by creating external functions for specific use cases, such as bulk transfers. 1. Diamonds are designed for tooling and user-interface software. -### New User-Interface Software & Libraries +#### New User-Interface Software & Libraries User-interface software can be written to show all documentation, functions and source code used by a diamond. @@ -284,7 +292,7 @@ Diamond events can be filtered from the Ethereum blockchain to show all changes Existing and new programming libraries and software can be used to deploy, show, upgrade and use diamonds. -### Upgradeable Diamond vs. Centralized Private Database +#### Upgradeable Diamond vs. Centralized Private Database Why have an upgradeable diamond instead of a centralized, private, mutable database? @@ -295,7 +303,7 @@ Why have an upgradeable diamond instead of a centralized, private, mutable datab 1. Independent security and domain experts can review the change history of contracts and vouch for their history of trustworthiness. 1. It is possible for an upgradeable diamond to become immutable and trustless. -### Different Kinds of Diamonds +#### Different Kinds of Diamonds Many designs of diamonds are possible. Here are a few kinds of diamonds and their uses. @@ -311,27 +319,27 @@ A single cut diamond adds all functions to itself in its constructor function, b 1. Your contract hits the max contract size limit. Make it into a single cut diamond. You still break your big contract into smaller facets, modularizing your code. 2. You start with an upgradeable diamond in your development and testing and upgrade it to your heart's delight. Reap the advantages of easy upgrading and a stable address as you work out new features, bugs and kinks. Release the upgradeable diamond on a test network with your application for beta testing and upgrade it when needed. This is iterative development. When it is solid then deploy it as a single cut diamond on the main network. -## Specification +### Specification > **Note:** > The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the contract storage of the calling contract, not the contract where the functions are defined. > **Note:** This specification specifies what needs to be implemented for a contract to be a diamond. -### Terms +#### Terms 1. A **diamond** is a contract that uses functions from its facets to execute function calls. A diamond can have one or more facets. 2. The word **facet** comes from the diamond industry. It is a side, or flat surface of a diamond. A diamond can have many facets. In this standard a facet is a contract with one or more functions that executes functionality of a diamond. 3. A **loupe** is a magnifying glass that is used to look at diamonds. In this standard a loupe is a facet that provides functions to look at a diamond and its facets. 4. An **immutable function** is a function that is defined directly in a diamond and so cannot be replaced or removed. Or it is a function that is defined in a facet that cannot be replaced or removed because all upgrade functions have been removed from a diamond. -### General Summary +#### General Summary A diamond calls functions from its facets using `delegatecall`. In the diamond industry diamonds are created and shaped by being cut, creating facets. In this standard diamonds are cut by adding, replacing or removing facets and their functions. -#### The diamondCut Function +##### The diamondCut Function The standard `diamondCut` function specified below can be used to add/replace/remove any number of functions from/to a diamond in a single transaction. @@ -347,7 +355,7 @@ If you want to create your own custom function(s) for adding/replacing/removing The `DiamondCut` event records all changes to a diamond. -### Diamond Interface +#### Diamond Interface ```Solidity interface IDiamondCut { @@ -376,7 +384,7 @@ interface IDiamondCut { } ``` -#### Adding/Replacing/Removing Functions +##### Adding/Replacing/Removing Functions A diamond contains within it a mapping of function selectors to facet addresses. Functions are added/replaced/removed by modifying this mapping. @@ -398,7 +406,7 @@ The `diamondCut` function reverts when attempting to replace a function with a f The `diamondCut` function reverts when attempting to remove a function that already does not exist. -#### Executing \_calldata +##### Executing \_calldata After adding/replacing/removing functions the `_calldata` argument is executed with `delegatecall` on `_init`. This execution is done to initialize data or setup or remove anything needed or no longer needed after adding, replacing and/or removing functions. @@ -406,13 +414,13 @@ If the `_init` value is `address(0)` then `_calldata` execution is skipped. In t If the `_init` value is not `address(0)` then `_calldata` must contain more than 0 bytes or the transaction reverts. -#### DiamondCut Event +##### DiamondCut Event The `_diamondCut`, `_init`, and `_calldata` arguments are passed directly to the `DiamondCut` event. Any time one or more functions are added, replaced or removed the `DiamondCut` event MUST be emitted to record changes. -### Diamond Loupe +#### Diamond Loupe > A loupe is a small magnifying glass used to look at diamonds. > These functions look at diamonds. @@ -462,7 +470,7 @@ The loupe functions can be used in deployment functionality, upgrade functionali Some loupe implementations are not gas efficient and should not be called in on-chain transactions. Some loupe implementations may be gas efficient and can be called in on-chain transactions. Read the documentation of the loupe implementation you use. -### Implementation Points +#### Implementation Points A diamond implements the following implementation points: @@ -476,7 +484,7 @@ A diamond implements the following implementation points: The diamond address is the address that users interact with. The diamond address does not change. Only facet addresses can change by using the `diamondCut` function, or other function. -## Implementation +## Reference Implementation Reference diamond implementations exist in the [Diamond repository](https://github.com/mudgen/Diamond). From 2ee260cd2f1280ae0283f6ba26c8dacf938663bc Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Wed, 8 Jun 2022 12:11:01 -0400 Subject: [PATCH 04/11] Better phrasing? --- EIPS/eip-2535.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index 4a3f95c85092a..c6cb6d28cc0f3 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -15,7 +15,7 @@ requires: 1538, 2535 ## Abstract -This EIP proposes a standard for creating modular smart contract systems that can be extended after deployment ("Diamonds"). This enables smart contracts to effectively have no size limit, and to be upgraded without having to redeploy existing functionality. A Diamond's "Facets" can be added, replaced, or removed without having to worry about unintended side-effects, providing a layer of abstraction that aids development. +This EIP proposes a standard for creating modular smart contract systems that can be extended after deployment ("Diamonds"). This enables smart contracts to effectively have no size limit, and to be upgraded without having to redeploy existing functionality. A Diamond's functions can be added, replaced, or removed individually, providing a layer of abstraction that aids development. ## Motivation From bf0bb9dc7c09ef3ce697fae5ce1cad726a1728e2 Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Wed, 8 Jun 2022 12:37:03 -0400 Subject: [PATCH 05/11] Edit EIP-2535 I am not done editing this, but I'll commit the changes for now --- EIPS/eip-2535.md | 75 +++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index c6cb6d28cc0f3..1cf23ec647a78 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -8,7 +8,7 @@ description: A standard for creating modular smart contract systems that can be type: Standards Track category: ERC created: 2020-02-22 -requires: 1538, 2535 +requires: 1538 --- @@ -19,55 +19,48 @@ This EIP proposes a standard for creating modular smart contract systems that ca ## Motivation -There are a number of different reasons to use diamonds. Here are some of them: +There are a number of different reasons to use diamonds: -1. **Diamonds can retain addresses through upgrades.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. -1. **Diamonds can exceed the maximum contract size (Currently 24KB).** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. -1. **Diamonds provide a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a gas-efficient way. -1. **Diamonds provide a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgradeable or immutable. It is also possible to make an upgradeable diamond and then at a later time remove its upgrade capability. +* **Diamonds can retain addresses through upgrades.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. +* **Diamonds can exceed the maximum contract size (Currently 24KB).** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. +* **Diamonds provide a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a gas-efficient way. +* **Diamonds provide a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgradeable or immutable. It is also possible to make an upgradeable diamond and then at a later time remove its upgrade capability. ## Specification The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. -## What is a Diamond? +### Definitions -A diamond is a contract with external functions that are supplied by contracts called **facets**. +A `Diamond` is a contract with external functions that MAY be supplied by `Facet` contracts. A `Facet` is a separate, independent contract that MAY share internal functions, libraries and state variables with other `Facet`s. -Facets are separate, independent contracts that can share internal functions, libraries and state variables. - -### Diamonds Support Transparency - -To find out what functions a regular smart contract has it is only necessary to look at its verified source code. - -The verified source code of a diamond does not include what functions it has so a different mechanism is needed. +### Transparency -A diamond has four standard functions called the loupe functions that are used to show what functions a diamond has. +The mechanism for verifying the contract code of a `Diamond` contract is inherently different than that of a traditional smart contract. Whereas looking at the source code would normally be sufficient to determine functionality, `Diamond`s require one to inspect the `Facet`s providing the `Diamond`'s methods. -The loupe functions can be used for many things including: -1. To show all functions used by a diamond. -1. To query services like Etherscan or files to retrieve and show all source code used by a diamond. -1. To query services like Etherscan or files to retrieve ABI information for a diamond. -1. To test or verify that a transaction that adds/replaces/removes functions on a diamond succeeded. -1. To find out what functions a diamond has before calling functions on it. -1. To be used by tools and programming libraries to deploy and upgrade diamonds. -1. To be used by user interfaces to show information about diamonds. -1. To be used by user interfaces to enable users to call functions on diamonds. +A `Diamond` MUST have four standard functions called the loop functions that are used to show what functions that `Diamond` implements. -Diamonds support another form of transparency which is a historical record of all upgrades on a diamond. This is done with the DiamondCut event which is used to record all functions that are added, replaced or removed on a diamond. +The following list provides example use-cases for these loop functions: -This standard is an improvement of [EIP-1538 Transparent Contract Standard](./eip-1538.md). The same motivations of that standard apply to this standard. +* To show all functions used by a diamond. +* To query services like Etherscan or files to retrieve and show all source code used by a diamond. +* To query services like Etherscan or files to retrieve ABI information for a diamond. +* To test or verify that a transaction that adds/replaces/removes functions on a diamond succeeded. +* To find out what functions a diamond has before calling functions on it. +* To be used by tools and programming libraries to deploy and upgrade diamonds. +* To be used by user interfaces to show information about diamonds. +* To be used by user interfaces to enable users to call functions on diamonds. -See the [Learning & References section](./eip-2535.md#learning--references) for additional information and uses of diamonds. +`Diamond` MUST provide another form of transparency through the `DiamondCut` event. This event is used to record all functions that are added, replaced or removed on a diamond. -### How a Diamond Works +### Fallback Function -A diamond stores within it a mapping of function selector to facet address, for example `selectorToFacet`. +A `Diamond` stores a mapping, `selectorToFacet`, of function selectors to `Facet` address. -When an external function is called on a diamond its fallback function is executed. The fallback function finds in the `selectorToFacet` mapping which facet has the function that has been called and then executes that function from the facet using `delegatecall`. +When an external function is called on a `Diamond`, the `Diamond`'s fallback function is executed. This fallback function fetches from the `selectorToFacet` mapping which `Facet` contains the reference of the function that has been called and then executes that function from the `Facet` using `delegatecall`. -A diamond's fallback function and `delegatecall` enable a diamond to execute a facet's external function as its own external function. The `msg.sender` and `msg.value` values do not change and only the diamond's contract storage is read and written to. +A `Diamond`'s fallback function and `delegatecall` enable a `Diamond` to execute a `Facet`'s external function as its own external function. The `msg.sender` and `msg.value` values remain unchanged,, and only the `Diamond`'s contract storage is read and written to. -Here is a simple example of a diamond's fallback function: +Here is an example of a `Diamond`'s fallback function: ```Solidity // Find facet for function that is called and execute the @@ -92,25 +85,21 @@ fallback() external payable { } ``` -A diamond can use a `diamondCut` function to add/replace/remove any number of functions from any number of facets in a single transaction. `diamondCut` updates the mapping of function selector to facet address. Other such functions can be used. +An event, `DiamondCut`, MUST be emitted any time external functions are added, replaced or removed, to report what changed. -An event is emitted any time external functions are added, replaced or removed to report what changed. +A `Diamond` MAY provide a `diamondCut` function to add/replace/remove any number of functions from any number of `Facet`s in a single transaction. `diamondCut` MUST update the mapping of function selector to facet address. Other functiona MAY change the mapping, but MUST still emit `DiamondCut`. -A diamond has four standard external functions that can be called to show what facets and functions it currently has. +A `Diamond` has four standard external functions that can be called to show what facets and functions it currently has. ### Upgrades and Immutability -A diamond can be immutable from inception by not adding any external functions that can add/replace/remove functions. [There are number of reasons to do this.](#different-kinds-of-diamonds) - -A diamond that is mutable can be made immutable by removing such functions. - -A diamond can be upgraded if it has a diamondCut external function or other function(s) that can add/replace/remove functions. +If an immutable `Diamond` is desired, one MAY be created whose function selector to `Facet` mapping is not changed by any functions. Existing `Diamond`s MAY become immutable by removing all methods that modify that mapping. ### Organizing State Variables in Diamonds -A state variable or storage layout organizational pattern is needed because Solidity's builtin storage layout system doesn't support proxy contracts or diamonds. +A state variable or storage layout organizational pattern is needed because Solidity's built-in storage layout system does not support proxy contracts or diamonds. -Described below are Diamond Storage and AppStorage which are two state variable organizational patterns that have been successfully used in diamonds. +Described below are `Diamond` Storage and AppStorage which are two state variable organizational patterns that have been successfully used in diamonds. Another successful pattern has been to write a storage contract that contains all state variables and is inherited by all facets. From b198b6b60abb4ed6f960a1d2dd3ac4322b63e0da Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Wed, 8 Jun 2022 12:39:54 -0400 Subject: [PATCH 06/11] Perhaps this phrasing is better? --- EIPS/eip-2535.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index 1cf23ec647a78..fb50152babf4e 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -15,7 +15,7 @@ requires: 1538 ## Abstract -This EIP proposes a standard for creating modular smart contract systems that can be extended after deployment ("Diamonds"). This enables smart contracts to effectively have no size limit, and to be upgraded without having to redeploy existing functionality. A Diamond's functions can be added, replaced, or removed individually, providing a layer of abstraction that aids development. +This EIP proposes a standard for creating modular smart contract systems that can be extended after deployment ("Diamonds"). This enables smart contracts to effectively have no size limit, and to be upgraded without having to redeploy existing functionality. Every function in a Diamond may be added, replaced, or removed, providing a layer of abstraction that aids development. ## Motivation From e83d958da367b57afea1b3bfa864e8435938b53a Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Wed, 8 Jun 2022 12:41:56 -0400 Subject: [PATCH 07/11] TIL what a loupe is --- EIPS/eip-2535.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index fb50152babf4e..a8374dba3db48 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -37,9 +37,9 @@ A `Diamond` is a contract with external functions that MAY be supplied by `Facet The mechanism for verifying the contract code of a `Diamond` contract is inherently different than that of a traditional smart contract. Whereas looking at the source code would normally be sufficient to determine functionality, `Diamond`s require one to inspect the `Facet`s providing the `Diamond`'s methods. -A `Diamond` MUST have four standard functions called the loop functions that are used to show what functions that `Diamond` implements. +A `Diamond` MUST have four standard functions called the loupe functions that are used to show what functions that `Diamond` implements. -The following list provides example use-cases for these loop functions: +The following list provides example use-cases for these loupe functions: * To show all functions used by a diamond. * To query services like Etherscan or files to retrieve and show all source code used by a diamond. From 349fe8f275f221e27de63e2bffa2922e59732620 Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Tue, 14 Jun 2022 08:35:31 -0400 Subject: [PATCH 08/11] Make quite a few more changes --- EIPS/eip-2535.md | 392 ++++++++--------------------------------------- 1 file changed, 65 insertions(+), 327 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index a8374dba3db48..e8d559c8c7223 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -19,27 +19,24 @@ This EIP proposes a standard for creating modular smart contract systems that ca ## Motivation -There are a number of different reasons to use diamonds: +There are a number of different reasons to use the `Diamond` standard: * **Diamonds can retain addresses through upgrades.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. * **Diamonds can exceed the maximum contract size (Currently 24KB).** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. * **Diamonds provide a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a gas-efficient way. * **Diamonds provide a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgradeable or immutable. It is also possible to make an upgradeable diamond and then at a later time remove its upgrade capability. -## Specification -The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. - ### Definitions -A `Diamond` is a contract with external functions that MAY be supplied by `Facet` contracts. A `Facet` is a separate, independent contract that MAY share internal functions, libraries and state variables with other `Facet`s. +A `Diamond` is a contract with external functions that may be supplied by `Facet` contracts. A `Facet` is a separate, independent contract that may share internal functions, libraries and state variables with other `Facet`s. ### Transparency The mechanism for verifying the contract code of a `Diamond` contract is inherently different than that of a traditional smart contract. Whereas looking at the source code would normally be sufficient to determine functionality, `Diamond`s require one to inspect the `Facet`s providing the `Diamond`'s methods. -A `Diamond` MUST have four standard functions called the loupe functions that are used to show what functions that `Diamond` implements. +All `Diamond`s have four standard functions called the loupe functions that are used to show what functions those `Diamond`s implement. -The following list provides example use-cases for these loupe functions: +The following list provides some example use-cases for these loupe functions: * To show all functions used by a diamond. * To query services like Etherscan or files to retrieve and show all source code used by a diamond. @@ -50,283 +47,98 @@ The following list provides example use-cases for these loupe functions: * To be used by user interfaces to show information about diamonds. * To be used by user interfaces to enable users to call functions on diamonds. -`Diamond` MUST provide another form of transparency through the `DiamondCut` event. This event is used to record all functions that are added, replaced or removed on a diamond. - -### Fallback Function - -A `Diamond` stores a mapping, `selectorToFacet`, of function selectors to `Facet` address. - -When an external function is called on a `Diamond`, the `Diamond`'s fallback function is executed. This fallback function fetches from the `selectorToFacet` mapping which `Facet` contains the reference of the function that has been called and then executes that function from the `Facet` using `delegatecall`. - -A `Diamond`'s fallback function and `delegatecall` enable a `Diamond` to execute a `Facet`'s external function as its own external function. The `msg.sender` and `msg.value` values remain unchanged,, and only the `Diamond`'s contract storage is read and written to. - -Here is an example of a `Diamond`'s fallback function: - -```Solidity -// Find facet for function that is called and execute the -// function if a facet is found and return any value. -fallback() external payable { - // get facet from function selector - address facet = selectorTofacet[msg.sig]; - require(facet != address(0)); - // Execute external function from facet using delegatecall and return any value. - assembly { - // copy function selector and any arguments - calldatacopy(0, 0, calldatasize()) - // execute function call using the facet - let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) - // get any return value - returndatacopy(0, 0, returndatasize()) - // return any return value or error back to the caller - switch result - case 0 {revert(0, returndatasize())} - default {return (0, returndatasize())} - } -} -``` - -An event, `DiamondCut`, MUST be emitted any time external functions are added, replaced or removed, to report what changed. - -A `Diamond` MAY provide a `diamondCut` function to add/replace/remove any number of functions from any number of `Facet`s in a single transaction. `diamondCut` MUST update the mapping of function selector to facet address. Other functiona MAY change the mapping, but MUST still emit `DiamondCut`. - -A `Diamond` has four standard external functions that can be called to show what facets and functions it currently has. +`Diamond`s provide another form of transparency through the `DiamondCut` event. This event is used to record all functions that are added, replaced or removed on a diamond. ### Upgrades and Immutability -If an immutable `Diamond` is desired, one MAY be created whose function selector to `Facet` mapping is not changed by any functions. Existing `Diamond`s MAY become immutable by removing all methods that modify that mapping. - -### Organizing State Variables in Diamonds - -A state variable or storage layout organizational pattern is needed because Solidity's built-in storage layout system does not support proxy contracts or diamonds. - -Described below are `Diamond` Storage and AppStorage which are two state variable organizational patterns that have been successfully used in diamonds. - -Another successful pattern has been to write a storage contract that contains all state variables and is inherited by all facets. - -### Facets, State Variables and Diamond Storage - -A facet defines external functions and can define or use internal functions, libraries, and state variables. - -A facet can declare state variables in structs. Each struct is given a specific position in contract storage. This technique is called **Diamond Storage**. - -Here is a simple example that shows Diamond Storage and its use in a facet: - -```Solidity -// A contract that implements Diamond Storage. -library LibA { - - // This struct contains state variables we care about. - struct DiamondStorage { - address owner; - bytes32 dataA; - } - - // Returns the struct from a specified position in contract storage - // ds is short for DiamondStorage - function diamondStorage() internal pure returns(DiamondStorage storage ds) { - // Specifies a random position in contract storage - // This can be done with a keccak256 hash of a unique string as is - // done here or other schemes can be used such as this: - // bytes32 storagePosition = keccak256(abi.encodePacked(ERC1155.interfaceId, ERC1155.name, address(this))); - bytes32 storagePosition = keccak256("diamond.storage.LibA"); - // Set the position of our struct in contract storage - assembly {ds.slot := storagePosition} - } -} - -// Our facet uses the Diamond Storage defined above. -contract FacetA { - - function setDataA(bytes32 _dataA) external { - LibA.DiamondStorage storage ds = LibA.diamondStorage(); - require(ds.owner == msg.sender, "Must be owner."); - ds.dataA = _dataA; - } - - function getDataA() external view returns (bytes32) { - return LibA.diamondStorage().dataA; - } -} -``` - -Any number of structs, each with a different storage position, can be used by a facet. - -By using Diamond Storage facets can declare their own state variables that do not conflict with the storage locations of state variables declared in other facets. - -By using Diamond Storage facets can be developed independently, without connection or concern for other facets. - -### AppStorage - -A specialized version of Diamond Storage is AppStorage. This pattern is used to more conveniently and easily share state variables between facets. - -The AppStorage pattern is implemented by defining a struct called AppStorage that contains the state variables of your application. It can contain any number of state variables of any type, including arrays and mappings, and more can be added in upgrades. - -The AppStorage struct is defined or imported and declared as the first and only state variable directly (or via inheritance) in any facet that uses it. This means that AppStorage is always located at position 0 in contract storage. - -The AppStorage state variable is often named `s` to provide easy access to it and to distinguish state variables from function arguments, function names, and local variables. - -Here is a simple example of a contract that uses AppStorage: - -```Solidity -import "./AppStorage.sol" - -contract StakingFacet { - AppStorage internal s; - - function myFacetFunction(uint256 _nextVar) external { - s.total = s.firstVar + _nextVar; - } - -``` - -The above example accesses the `s.firstVar` state variable and stores a computation in the `s.total` state variable. - -Read this article to learn more about AppStorage: [AppStorage Pattern for State Variables in Solidity](https://dev.to/mudgen/appstorage-pattern-for-state-variables-in-solidity-3lki). - - -### Diamonds Can Use Other Contract Storage Strategies - -Diamonds and facets don't have to use Diamond Storage or AppStorage. They can use or mix with other contract storage strategies such as contract inheritance. - -Diamonds only need to implement the [Specification section](#specification) of this standard. - -### Facets Can Share State and Functionality - -Facets can share state variables by using the same structs at the same storage positions. - -Facets can share internal functions and libraries by inheriting the same contracts or using the same libraries. - -In these ways facets are separate, independent units but can share state and functionality. - -### Facets are Reusable and Composable - -A deployed facet can be used by any number of diamonds. +If an immutable `Diamond` is desired, one can be created whose function selector to `Facet` mapping is not changed by any functions. Existing `Diamond`s can become immutable by removing all methods that modify that mapping. -Different combinations of facets can be used with different diamonds. - -It is possible to create and deploy a set of facets that are reused by different diamonds over time. - -The ability to use the same deployed facets for many diamonds reduces deployment costs. - -A limitation is that two external functions with the same function signature can't be added to a diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature. - -### Diagrams +### Visual Representations of Diamonds #### Diamond Structure -This diagram shows the structure of a diamond: +This figure shows the structure of a diamond: #### Diamond Storage -The diagram below shows facets with their own data and data shared between them. - -Notice that all data is stored in the diamond. But different facets have different access to data. - -In this diagram +The following figure demonstrates a storage pattern with isolated and shared data: -- Only FacetA can access DataA -- Only FacetB can access DataB -- Only the diamond's own code can access DataD. -- FacetA and FacetB share access to DataAB. -- The diamond's own code, FacetA and FacetB share access to DataABD. +- Only `FacetA` can access `DataA` +- Only `FacetB` can access `DataB` +- Only the `Diamond`'s own code can access `DataD`. +- Both `FacetA` and `FacetB` share access to `DataAB`. +- The diamond's own code, `FacetA` and `FacetB` share access to `DataABD`. #### Deployed Facets Can Be Reused -A deployed facet can be used by any number of diamonds. +The following figure demonstrates two `Diamond`s using the same two `Facet`s. -The diagram below shows two diamonds using the same two facets. - -- FacetA is used by Diamond1 -- FacetA is used by Diamond2 -- FacetB is used by Diamond1 -- FacetB is used by Diamond2 +- `FacetA` is used by `Diamond1` +- `FacetA` is used by `Diamond2` +- `FacetB` is used by `Diamond1` +- `FacetB` is used by `Diamond2` -#### Some Diamond Benefits - -1. A stable contract address that provides needed functionality. -1. A single address with the functionality of multiple contracts (facets) that are independent from each other but can share internal functions, libraries and state variables. -1. A way to add, replace and remove multiple external functions atomically (in the same transaction). -1. Fine-grained upgrades, so you can change just the parts of a diamond that need to be changed. -1. Have greater control over when and what functions exist. -1. Decentralized Autonomous Organizations (DAOs) and other governance systems can be used to upgrade diamonds. -1. An event that shows what functions are added, replaced and removed. -1. The ability to show all changes made to a diamond. -1. Increase trust over time by showing all changes made to a diamond. -1. A way to look at a diamond to see its current facets and functions. -1. Have an immutable, trustless diamond. -1. Solves the 24KB maximum contract size limitation. Diamonds can be any size. -1. Separate functionality can be implemented in separate facets and used together in a diamond. -1. Larger contracts have to reduce their size by removing error messages and other things. You can keep your error messages and the full functionality that you need by implementing a diamond. -1. Enables zero, partial or full diamond immutability as desired, and when desired. -1. The ability to develop and improve an application over time with an upgradeable diamond and then make it immutable and trustless if desired. -1. Develop incrementally and let your diamond grow with your application. -1. Upgrade diamonds to fix bugs, add functionality and implement new standards. -1. Organize your code with a diamond and facets. -1. Diamonds can be large (have many functions) but still be modular because they are compartmented with facets. -1. Contract architectures that call multiple contracts in a single transaction can save gas by condensing those contracts into a single diamond and accessing state variables directly. -1. Save gas by creating external functions for specific use cases, such as bulk transfers. -1. Diamonds are designed for tooling and user-interface software. - -#### New User-Interface Software & Libraries - -User-interface software can be written to show all documentation, functions and source code used by a diamond. - -Diamond events can be filtered from the Ethereum blockchain to show all changes to a diamond. - -Existing and new programming libraries and software can be used to deploy, show, upgrade and use diamonds. - -#### Upgradeable Diamond vs. Centralized Private Database - -Why have an upgradeable diamond instead of a centralized, private, mutable database? - -1. Decentralized Autonomous Organizations (DAOs) and other governance systems can be used to upgrade diamonds. -1. Wide interaction and integration with the Ethereum ecosystem. -1. With open storage data and verified source code it is possible to show a provable history of trustworthiness. -1. With openness bad behavior can be spotted and reported when it happens. -1. Independent security and domain experts can review the change history of contracts and vouch for their history of trustworthiness. -1. It is possible for an upgradeable diamond to become immutable and trustless. +## Specification +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. -#### Different Kinds of Diamonds +> **Note:** +> The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the contract storage of the calling contract instead of the contract where the functions are defined. -Many designs of diamonds are possible. Here are a few kinds of diamonds and their uses. +### Definitions -**Upgradeable Diamond** -An upgradeable diamond has the `diamondCut` function and/or possibly other functions to add/replace/remove functions. It is useful for iterative development or improving an application over time. +1. A `Diamond` is a contract that uses functions from its `Facet` to execute function calls. A `Diamond` NAY have any number of `Facet`s. +2. A `Facet` is a contract containing the implementation code of one or more functions of a `Diamond`. +3. The **loupe functions** are functions of a `Diamond` that MAY be used to inspect the implementation code of that diamond. +4. An **immutable function** is a function that is defined in a `Diamond`'s contract, or whose corresponding `Facet` cannot be changed. -**Finished Diamond** -A finished diamond was an upgradeable diamond and had a number of upgrades. Then its `diamondCut` function and/or other upgrade functions were removed and upgrades are no longer possible. It is no longer possible to add/replace/remove functions. It has become an immutable diamond. +#### General Summary -**Single Cut Diamond** -A single cut diamond adds all functions to itself in its constructor function, but it does not add the `diamondCut` function or any other function that can add/replace/remove functions. This means that a single cut diamond is fully created in its constructor and once created can never be upgraded. It has the same immutability and trustless guarantees as a regular vanilla contract. Why would someone do this? There may be a number of reasons. The two use cases below are good reasons. +A `Diamond` calls functions from its `Facet`s using `delegatecall`. `Diamond`s are cut by adding, replacing or removing `Facet`s and their functions. -1. Your contract hits the max contract size limit. Make it into a single cut diamond. You still break your big contract into smaller facets, modularizing your code. -2. You start with an upgradeable diamond in your development and testing and upgrade it to your heart's delight. Reap the advantages of easy upgrading and a stable address as you work out new features, bugs and kinks. Release the upgradeable diamond on a test network with your application for beta testing and upgrade it when needed. This is iterative development. When it is solid then deploy it as a single cut diamond on the main network. +### Fallback Function -### Specification +A `Diamond` stores a mapping of function selectors to `Facet` address. -> **Note:** -> The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the contract storage of the calling contract, not the contract where the functions are defined. +When an external function is called on a `Diamond`, the `Diamond`'s fallback function is executed. This fallback function determines the `Facet` containing the reference of the function that has been called and then executes that function from the `Facet` using `delegatecall`. -> **Note:** This specification specifies what needs to be implemented for a contract to be a diamond. +A `Diamond`'s fallback function and `delegatecall` enable a `Diamond` to execute a `Facet`'s external function as its own external function. The `msg.sender` and `msg.value` values remain unchanged, and only the `Diamond`'s contract storage is read and written to. -#### Terms +Here is an example of a `Diamond`'s fallback function: -1. A **diamond** is a contract that uses functions from its facets to execute function calls. A diamond can have one or more facets. -2. The word **facet** comes from the diamond industry. It is a side, or flat surface of a diamond. A diamond can have many facets. In this standard a facet is a contract with one or more functions that executes functionality of a diamond. -3. A **loupe** is a magnifying glass that is used to look at diamonds. In this standard a loupe is a facet that provides functions to look at a diamond and its facets. -4. An **immutable function** is a function that is defined directly in a diamond and so cannot be replaced or removed. Or it is a function that is defined in a facet that cannot be replaced or removed because all upgrade functions have been removed from a diamond. +```Solidity +// Find facet for function that is called and execute the +// function if a facet is found and return any value. +fallback() external payable { + // get facet from function selector + address facet = selectorTofacet[msg.sig]; + require(facet != address(0)); + // Execute external function from facet using delegatecall and return any value. + assembly { + // copy function selector and any arguments + calldatacopy(0, 0, calldatasize()) + // execute function call using the facet + let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) + // get any return value + returndatacopy(0, 0, returndatasize()) + // return any return value or error back to the caller + switch result + case 0 {revert(0, returndatasize())} + default {return (0, returndatasize())} + } +} +``` -#### General Summary +An event, `DiamondCut`, MUST be emitted any time external functions are added, replaced or removed, to report what changed. -A diamond calls functions from its facets using `delegatecall`. +A `Diamond` MAY provide a `diamondCut` function to add/replace/remove any number of functions from any number of `Facet`s in a single transaction. `diamondCut` MUST update the mapping of function selector to facet address. Other functiona MAY change the mapping, but MUST still emit `DiamondCut`. -In the diamond industry diamonds are created and shaped by being cut, creating facets. In this standard diamonds are cut by adding, replacing or removing facets and their functions. +A `Diamond` has four standard external functions that can be called to show what facets and functions it currently has. ##### The diamondCut Function @@ -479,11 +291,13 @@ Reference diamond implementations exist in the [Diamond repository](https://gith ## Rationale -### Using Function Selectors +EIP-2535 is an improved design over [EIP-1538](./eip-1538.md) using `ABIEncoderV2` and function selectors. EIP-2535 supersedes EIP-1538. + +This standard was influenced by [EIP-1538](./eip-1538.md) and ZeppelinOS's implementation of upgradeability with vtables and the Mokens contract. -User interface software can be used to retrieve function selectors and face addresses from a diamond in order show what functions a diamond has. +### Usage of function Selectors -This standard is designed to make diamonds work well with user-interface software. Function selectors with the ABI of a contract provide enough information about functions to be useful for user-interface software. +Developer tools can use the loupe functions to enumerate the functions a `Diamond` has implemented and the corresponding `Facet`s. ### Gas Considerations @@ -580,82 +394,6 @@ Diamonds emit an event every time one or more functions are added, replaced or r Security and domain experts can review the history of change of a diamond to detect any history of foul play. -## Backwards Compatibility - -This standard makes upgradeable diamonds compatible with future standards and functionality because new functions can be added and existing functions can be replaced or removed. - -## Learning & References - -### Diamond Articles - -[Introduction to EIP-2535 Diamonds](https://eip2535diamonds.substack.com/p/introduction-to-the-diamond-standard) - -[Smart Contract Security Audits for EIP-2535 Diamonds Implementations](https://eip2535diamonds.substack.com/p/smart-contract-security-audits-for) - -[Why Ethereum Diamonds Need A Standard](https://dev.to/mudgen/why-diamonds-need-a-standard-1i1h) - -[Ethereum's Maximum Contract Size Limit is Solved with EIP-2535 Diamonds](https://dev.to/mudgen/ethereum-s-maximum-contract-size-limit-is-solved-with-the-diamond-standard-2189) - -[Understanding Diamonds](https://dev.to/mudgen/understanding-diamonds-on-ethereum-1fb) - -[Why Ethereum Diamonds Need A Standard](https://dev.to/mudgen/why-diamonds-need-a-standard-1i1h) - -[Diamond Loupe Functions?](https://dev.to/mudgen/why-loupe-functions-for-diamonds-1kc3) - -[EIP-2535 Diamonds: A new paradigm for upgradeability](https://medium.com/derivadex/the-diamond-standard-a-new-paradigm-for-upgradeability-569121a08954) - -[Upgradeable smart contracts using EIP-2535 Diamonds](https://hiddentao.com/archives/2020/05/28/upgradeable-smart-contracts-using-diamond-standard) - -[Ethereum Diamonds for On-Chain Decentralized Governance](https://dev.to/mudgen/ethereum-diamonds-for-on-chain-decentralized-governance-39op) - -[How to Share Functions Between Facets of a Diamond](https://dev.to/mudgen/how-to-share-functions-between-facets-of-a-diamond-1njb) - -### Diamond Storage Articles - -[How Diamond Storage Works](https://dev.to/mudgen/how-diamond-storage-works-90e) - -[AppStorage Pattern for State Variables in Solidity](https://dev.to/mudgen/appstorage-pattern-for-state-variables-in-solidity-3lki) - -[New Storage Layout For Proxy Contracts and Diamonds](https://medium.com/1milliondevs/new-storage-layout-for-proxy-contracts-and-diamonds-98d01d0eadb) - -[Solidity Libraries Can't Have State Variables -- Oh Yes They Can!](https://dev.to/mudgen/solidity-libraries-can-t-have-state-variables-oh-yes-they-can-3ke9) - -[ Smart Contracts Sharing Common Data](https://medium.com/coinmonks/smart-contracts-sharing-common-data-777310263ac0?source=friends_link&sk=b462ff3559ae9c8da243ba31a557e4f4) - -[Sharing Common Data Using Libraries](https://medium.com/coinmonks/sharing-common-data-using-libraries-6573857d328c?source=friends_link&sk=1da1ef153b8d15f3f7bb9f4ce429890a) - -### Diamond Tools - -[Louper](https://louper.dev/) A user interface for all diamonds. - -[buidler-deploy](https://github.com/wighawag/buidler-deploy#diamonds-and-facets) - -### Implementations - -[Diamond reference implementations](https://github.com/mudgen/Diamond) - -[GHST Staking](https://github.com/aavegotchi/ghst-staking) - -[pie-dao / ExperiPie](https://github.com/pie-dao/ExperiPie) - -[Nayms Contracts](https://github.com/nayms/contracts) - -[Aavegotchi Diamond](https://github.com/aavegotchi/aavegotchi-contracts) - -### Help - -[EIP-2535 Diamonds Discord](https://discord.gg/kQewPw2) - -## Inspiration & Development - -EIP-2535 is an improved design over [EIP-1538](./eip-1538.md) using ABIEncoderV2 and function selectors. - -EIP-2535 replaces EIP-1538. - -This standard was inspired by [EIP-1538](./eip-1538.md) and ZeppelinOS's implementation of [Upgradeability with vtables](https://github.com/zeppelinos/labs/tree/master/upgradeability_with_vtable). - -This standard was also inspired by the design and implementation of the [Mokens contract](https://etherscan.io/address/0xc1eab49cf9d2e23e43bcf23b36b2be14fc2f8838#code). - ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). From ac1ee809b790427117cba0ffd336ae0e672188bc Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Tue, 14 Jun 2022 08:56:21 -0400 Subject: [PATCH 09/11] More organization --- EIPS/eip-2535.md | 117 ++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index e8d559c8c7223..b74b8ba3905b3 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -90,16 +90,45 @@ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL > **Note:** > The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the contract storage of the calling contract instead of the contract where the functions are defined. +### General Summary + +A `Diamond` calls functions from its `Facet`s using `delegatecall`. `Diamond`s are cut by adding, replacing or removing `Facet`s and their functions. + ### Definitions -1. A `Diamond` is a contract that uses functions from its `Facet` to execute function calls. A `Diamond` NAY have any number of `Facet`s. -2. A `Facet` is a contract containing the implementation code of one or more functions of a `Diamond`. -3. The **loupe functions** are functions of a `Diamond` that MAY be used to inspect the implementation code of that diamond. -4. An **immutable function** is a function that is defined in a `Diamond`'s contract, or whose corresponding `Facet` cannot be changed. +1. A `Diamond` is a contract that uses functions from its `Facet` to execute function calls. A `Diamond` MAY have any number of `Facet`s. +1. A `Facet` is a contract containing the implementation code of one or more functions of a `Diamond`. +1. The **loupe functions** are functions of a `Diamond` that MAY be used to inspect the implementation code of that diamond. +1. An **immutable function** is a function that is defined in a `Diamond`'s contract, or whose corresponding `Facet` cannot be changed. -#### General Summary +### Diamond Interface -A `Diamond` calls functions from its `Facet`s using `delegatecall`. `Diamond`s are cut by adding, replacing or removing `Facet`s and their functions. +```Solidity +interface IDiamondCut { + enum FacetCutAction {Add, Replace, Remove} + // Add=0, Replace=1, Remove=2 + + struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; + } + + /// @notice Add/replace/remove any number of functions and optionally execute + /// a function with delegatecall + /// @param _diamondCut Contains the facet addresses and function selectors + /// @param _init The address of the contract or facet to execute _calldata + /// @param _calldata A function call, including function selector and arguments + /// _calldata is executed with delegatecall on _init + function diamondCut( + FacetCut[] calldata _diamondCut, + address _init, + bytes calldata _calldata + ) external; + + event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); +} +``` ### Fallback Function @@ -140,52 +169,7 @@ A `Diamond` MAY provide a `diamondCut` function to add/replace/remove any number A `Diamond` has four standard external functions that can be called to show what facets and functions it currently has. -##### The diamondCut Function - -The standard `diamondCut` function specified below can be used to add/replace/remove any number of functions from/to a diamond in a single transaction. - -The standard `diamondCut` function below is specified for the purpose of interoperability. Diamond tools, software and user-interfaces should expect and use the standard `diamondCut` function. Diamonds that might work with diamond specific tooling to add/replace/remove functions should implement the standard `diamondCut` function. - -The `diamondCut` function is optional. It does not have to be implemented in a diamond. For example an immutable diamond wouldn't have this function. - -You can implement your own custom functions that add or replace or remove functions. You can also implement your own non-standard versions of `diamondCut` that have different parameters. - -If you want to create your own custom function(s) for adding/replacing/removing functions you might also want to implement the standard `diamondCut` function for interoperability with tools. - -**In ALL cases any function or code that adds or replaces or removes one or more functions MUST emit the standard DiamondCut event specified below.** - -The `DiamondCut` event records all changes to a diamond. - -#### Diamond Interface - -```Solidity -interface IDiamondCut { - enum FacetCutAction {Add, Replace, Remove} - // Add=0, Replace=1, Remove=2 - - struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; - } - - /// @notice Add/replace/remove any number of functions and optionally execute - /// a function with delegatecall - /// @param _diamondCut Contains the facet addresses and function selectors - /// @param _init The address of the contract or facet to execute _calldata - /// @param _calldata A function call, including function selector and arguments - /// _calldata is executed with delegatecall on _init - function diamondCut( - FacetCut[] calldata _diamondCut, - address _init, - bytes calldata _calldata - ) external; - - event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); -} -``` - -##### Adding/Replacing/Removing Functions +### Adding/Replacing/Removing Functions A diamond contains within it a mapping of function selectors to facet addresses. Functions are added/replaced/removed by modifying this mapping. @@ -207,7 +191,7 @@ The `diamondCut` function reverts when attempting to replace a function with a f The `diamondCut` function reverts when attempting to remove a function that already does not exist. -##### Executing \_calldata +### Executing \_calldata After adding/replacing/removing functions the `_calldata` argument is executed with `delegatecall` on `_init`. This execution is done to initialize data or setup or remove anything needed or no longer needed after adding, replacing and/or removing functions. @@ -215,13 +199,25 @@ If the `_init` value is `address(0)` then `_calldata` execution is skipped. In t If the `_init` value is not `address(0)` then `_calldata` must contain more than 0 bytes or the transaction reverts. -##### DiamondCut Event +### The `diamondCut` Function + +The standard `diamondCut` function specified below can be used to add/replace/remove any number of functions from/to a diamond in a single transaction. + +The standard `diamondCut` function below is specified for the purpose of interoperability. Diamond tools, software and user-interfaces should expect and use the standard `diamondCut` function. Diamonds that might work with diamond specific tooling to add/replace/remove functions should implement the standard `diamondCut` function. + +The `diamondCut` function is optional. It does not have to be implemented in a diamond. For example an immutable diamond wouldn't have this function. + +You can implement your own custom functions that add or replace or remove functions. You can also implement your own non-standard versions of `diamondCut` that have different parameters. + +If you want to create your own custom function(s) for adding/replacing/removing functions you might also want to implement the standard `diamondCut` function for interoperability with tools. + +### DiamondCut Event The `_diamondCut`, `_init`, and `_calldata` arguments are passed directly to the `DiamondCut` event. Any time one or more functions are added, replaced or removed the `DiamondCut` event MUST be emitted to record changes. -#### Diamond Loupe +### Diamond Loupe > A loupe is a small magnifying glass used to look at diamonds. > These functions look at diamonds. @@ -271,7 +267,7 @@ The loupe functions can be used in deployment functionality, upgrade functionali Some loupe implementations are not gas efficient and should not be called in on-chain transactions. Some loupe implementations may be gas efficient and can be called in on-chain transactions. Read the documentation of the loupe implementation you use. -#### Implementation Points +### Implementation Points A diamond implements the following implementation points: @@ -303,9 +299,9 @@ Developer tools can use the loupe functions to enumerate the functions a `Diamon Delegating function calls does have some gas overhead. This is mitigated in several ways: -1. Because diamonds do not have a max size limitation it is possible to add gas optimizing functions for use cases. For example someone could use a diamond to implement the ERC721 standard and implement batch transfer functions from the [ERC1412 standard](https://github.com/ethereum/EIPs/issues/1412) to reduce gas (and make batch transfers more convenient). +1. Because `Diamond`s do not have a max size limitation it is possible to add gas optimizing functions for use cases. For example someone could use a `Diamond` to implement the [EIP-721](./eip-721.md) standard and implement batch transfer functions to reduce gas (and make batch transfers more convenient). 1. Some contract architectures require calling many contracts in one transaction. Gas savings can be realized by condensing those contracts into a single diamond and accessing contract storage directly. -1. Facets can be small, reducing gas costs. Because it costs more gas to call a function in a contract with many functions than a contract with few functions. +1. `Facet`s can be small, reducing gas costs due to scaling of gas costs with the numer of functions in a contract. 1. The Solidity optimizer can be set to a high setting causing more bytecode to be generated but the facets will use less gas when executed. ### Diamond Storage @@ -337,7 +333,7 @@ bytes memory myFunctionCall = abi.encodeWithSelector(functionSelector, 4); (bool success, uint result) = address(facet).delegatecall(myFunctionCall); require(success, "myFunction failed"); ``` -6. Instead of calling an external function defined in another facet you can instead create an internal function version of the external function. Add the internal version of the function to the facet that needs to use it. +1. Instead of calling an external function defined in another facet you can instead create an internal function version of the external function. Add the internal version of the function to the facet that needs to use it. ### Default Function @@ -391,9 +387,6 @@ A function selector clash occurs when two different function signatures hash to ### Transparency Diamonds emit an event every time one or more functions are added, replaced or removed. All source code can be verified. This enables people and software to monitor changes to a contract. If any bad acting function is added to a diamond then it can be seen. - -Security and domain experts can review the history of change of a diamond to detect any history of foul play. - ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). From ea1ce60c5c02a02bd98c91601248d3ea86a8b315 Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Thu, 16 Jun 2022 07:32:16 -0400 Subject: [PATCH 10/11] Apply suggestions from code review Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> --- EIPS/eip-2535.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index b74b8ba3905b3..adeddbf597d99 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -22,7 +22,7 @@ This EIP proposes a standard for creating modular smart contract systems that ca There are a number of different reasons to use the `Diamond` standard: * **Diamonds can retain addresses through upgrades.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. -* **Diamonds can exceed the maximum contract size (Currently 24KB).** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. +* **Diamonds can exceed the maximum contract size (currently 24KB).** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. * **Diamonds provide a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a gas-efficient way. * **Diamonds provide a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgradeable or immutable. It is also possible to make an upgradeable diamond and then at a later time remove its upgrade capability. @@ -211,7 +211,7 @@ You can implement your own custom functions that add or replace or remove functi If you want to create your own custom function(s) for adding/replacing/removing functions you might also want to implement the standard `diamondCut` function for interoperability with tools. -### DiamondCut Event +### `DiamondCut` Event The `_diamondCut`, `_init`, and `_calldata` arguments are passed directly to the `DiamondCut` event. @@ -301,7 +301,7 @@ Delegating function calls does have some gas overhead. This is mitigated in seve 1. Because `Diamond`s do not have a max size limitation it is possible to add gas optimizing functions for use cases. For example someone could use a `Diamond` to implement the [EIP-721](./eip-721.md) standard and implement batch transfer functions to reduce gas (and make batch transfers more convenient). 1. Some contract architectures require calling many contracts in one transaction. Gas savings can be realized by condensing those contracts into a single diamond and accessing contract storage directly. -1. `Facet`s can be small, reducing gas costs due to scaling of gas costs with the numer of functions in a contract. +1. `Facet`s can be small, reducing gas costs due to scaling of gas costs with the number of functions in a contract. 1. The Solidity optimizer can be set to a high setting causing more bytecode to be generated but the facets will use less gas when executed. ### Diamond Storage From ed622d9ee3cde4881d82db306b466d8bf20beac6 Mon Sep 17 00:00:00 2001 From: Pandapip1 <45835846+Pandapip1@users.noreply.github.com> Date: Thu, 16 Jun 2022 07:35:35 -0400 Subject: [PATCH 11/11] Make all other changes from review --- EIPS/eip-2535.md | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md index adeddbf597d99..9b711af51449b 100644 --- a/EIPS/eip-2535.md +++ b/EIPS/eip-2535.md @@ -87,9 +87,6 @@ The following figure demonstrates two `Diamond`s using the same two `Facet`s. ## Specification The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. -> **Note:** -> The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the contract storage of the calling contract instead of the contract where the functions are defined. - ### General Summary A `Diamond` calls functions from its `Facet`s using `delegatecall`. `Diamond`s are cut by adding, replacing or removing `Facet`s and their functions. @@ -259,8 +256,6 @@ interface IDiamondLoupe { } ``` -See a [reference implementation](https://github.com/mudgen/Diamond) to see how this can be implemented. - The loupe functions can be used in user-interface software. A user interface calls these functions to provide information about and visualize diamonds. The loupe functions can be used in deployment functionality, upgrade functionality, testing and other software. @@ -281,19 +276,20 @@ A diamond implements the following implementation points: The diamond address is the address that users interact with. The diamond address does not change. Only facet addresses can change by using the `diamondCut` function, or other function. -## Reference Implementation - -Reference diamond implementations exist in the [Diamond repository](https://github.com/mudgen/Diamond). - ## Rationale -EIP-2535 is an improved design over [EIP-1538](./eip-1538.md) using `ABIEncoderV2` and function selectors. EIP-2535 supersedes EIP-1538. +This EIP is an improved design over [EIP-1538](./eip-1538.md) using `ABIEncoderV2` and function selectors. This EIP supersedes EIP-1538. This standard was influenced by [EIP-1538](./eip-1538.md) and ZeppelinOS's implementation of upgradeability with vtables and the Mokens contract. +## Reference Implementation + + +Reference diamond implementations exist in the [Diamond repository](https://github.com/mudgen/Diamond). + ### Usage of function Selectors -Developer tools can use the loupe functions to enumerate the functions a `Diamond` has implemented and the corresponding `Facet`s. +Developer tools can use the loupe functions to enumerate the functions a `Diamond` has implemented and their corresponding `Facet`s. ### Gas Considerations @@ -306,7 +302,7 @@ Delegating function calls does have some gas overhead. This is mitigated in seve ### Diamond Storage -Since Solidity 0.6.4 it is possible to create pointers to structs in arbitrary places in contract storage. This enables diamonds and their facets to create their own storage layouts that are separate from each other and do not conflict with each other, but can still be shared between them. See this blog post for more information: [New Storage Layout For Proxy Contracts and Diamonds](https://medium.com/1milliondevs/new-storage-layout-for-proxy-contracts-and-diamonds-98d01d0eadb). The reference implementations for EIP-2535 uses Diamond Storage. +Since Solidity 0.6.4 it is possible to create pointers to structs in arbitrary places in contract storage. This enables diamonds and their facets to create their own storage layouts that are separate from each other and do not conflict with each other, but can still be shared between them. See this blog post for more information. The reference implementation uses Diamond Storage. Diamond Storage is not the same thing as unstructured storage. Unstructured storage reads and writes specific values like unsigned integers and addresses at specified locations in contract storage. Diamond Storage uses structs at specified locations in contract storage for reading and writing. Structs can hold any number of state variables of any type. @@ -347,7 +343,7 @@ A default function can be implemented a number of ways and this standard does no > **Note:** The design and implementation of diamond ownership/authentication is **not** part of this standard. The examples given in this standard and in the reference implementation are just **examples** of how it could be done. -It is possible to create many different authentication or ownership schemes with EIP-2535. Authentication schemes can be very simple or complex, fine grained or coarse. EIP-2535 does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to only add/replace/remove certain functions. +It is possible to create many different authentication or ownership schemes with this EIP. Authentication schemes can be very simple or complex, fine grained or coarse. This EIP does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to only add/replace/remove certain functions. Consensus functionality could be implemented such as an approval function that multiple different people call to approve changes before they are executed with the `diamondCut` function. These are just examples.