From 42b70278ff35fbe74c039f31d33aec05471d8bad Mon Sep 17 00:00:00 2001 From: maxnorm Date: Tue, 30 Dec 2025 18:45:00 +0000 Subject: [PATCH] docs: auto-generate docs pages from NatSpec --- .../library/diamond/DiamondInspectFacet.mdx | 91 ++- .../library/diamond/DiamondLoupeFacet.mdx | 100 +-- website/docs/library/diamond/DiamondMod.mdx | 139 ++--- .../library/diamond/DiamondUpgradeFacet.mdx | 517 +++++++++++++++ .../library/diamond/DiamondUpgradeMod.mdx | 586 ++++++++++++++++++ .../diamond/example/ExampleDiamond.mdx | 68 +- .../docs/library/diamond/example/index.mdx | 2 +- website/docs/library/diamond/index.mdx | 20 +- 8 files changed, 1311 insertions(+), 212 deletions(-) create mode 100644 website/docs/library/diamond/DiamondUpgradeFacet.mdx create mode 100644 website/docs/library/diamond/DiamondUpgradeMod.mdx diff --git a/website/docs/library/diamond/DiamondInspectFacet.mdx b/website/docs/library/diamond/DiamondInspectFacet.mdx index 15013528..a151f128 100644 --- a/website/docs/library/diamond/DiamondInspectFacet.mdx +++ b/website/docs/library/diamond/DiamondInspectFacet.mdx @@ -1,7 +1,7 @@ --- sidebar_position: 510 title: "DiamondInspectFacet" -description: "Inspect diamond state and facet mappings" +description: "Inspect diamond storage and facet mappings" gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondInspectFacet.sol" --- @@ -21,19 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Inspect diamond state and facet mappings +Inspect diamond storage and facet mappings -- Exposes external view function `functionFacetPairs` for diamond introspection. -- Provides internal function `getStorage` for direct diamond storage retrieval. -- Self-contained with no external dependencies. -- Follows Compose readability-first conventions. +- Exposes external functions for diamond inspection. +- Retrieves facet address for a given function selector. +- Lists all registered function selectors and their corresponding facet addresses. +- Utilizes inline assembly for efficient storage access. ## Overview -This facet provides external functions to inspect the diamond's storage layout and its registered function-to-facet mappings. It routes calls through the diamond proxy, allowing developers to query the diamond's structure without direct storage access. Add this facet to expose introspection capabilities while maintaining upgradeability. +This facet provides essential inspection capabilities for a diamond proxy, enabling developers to query storage and facet mappings. It exposes functions to retrieve the diamond's storage struct, determine the facet handling a specific function selector, and list all function-to-facet pairs. This is crucial for understanding and interacting with the diamond's internal state and facet distribution. --- @@ -78,7 +78,7 @@ This facet provides external functions to inspect the diamond's storage layout a { name: "DIAMOND_STORAGE_POSITION", type: "bytes32", - description: "Diamond storage slot position for this module (Value: `keccak256(\"compose.diamond\")`)" + description: "Diamond storage slot position for this module (Value: `keccak256(\"erc8109.diamond\")`)" } ]} showRequired={false} @@ -86,6 +86,41 @@ This facet provides external functions to inspect the diamond's storage layout a ## Functions +### facetAddress + +Gets the facet address that handles the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- ### functionFacetPairs Returns an array of all function selectors and their corresponding facet addresses. Iterates through the diamond's stored selectors and pairs each with its facet. @@ -114,29 +149,31 @@ Returns an array of all function selectors and their corresponding facet address {`pragma solidity >=0.8.30; -import "@compose/diamond/DiamondInspectFacet"; -import "@compose/diamond/IDiamond"; +import {IDiamond} from "@compose/diamond/IDiamond"; +import {DiamondInspectFacet} from "@compose/diamond/DiamondInspectFacet"; -contract DiamondUser { - address immutable diamondAddress; +contract DiamondInspector { + address public immutable diamondAddress; constructor(address _diamondAddress) { diamondAddress = _diamondAddress; } - function getFacetPairs() public view returns (DiamondInspectFacet.FunctionFacetPair[] memory) { + function inspectDiamond() external view { IDiamond diamond = IDiamond(diamondAddress); - // Calls the getFunctionFacetPairs function through the diamond proxy - return diamond.functionFacetPairs(); - } - function inspectStorage() public pure { - // Note: getStorage is internal and not directly callable via the diamond proxy. - // It's intended for internal facet logic or direct contract interaction. - // Example of direct call if you had the facet address: - // DiamondInspectFacet inspectFacet = DiamondInspectFacet(facetAddress); - // DiamondStorage storageLayout = inspectFacet.getStorage(); + // Get the facet address for a specific function selector + address ownerFacet = diamond.facetAddress(bytes4(keccak256("owner()"))); + // Get all function-to-facet pairs + DiamondInspectFacet.FunctionFacetPair[] memory pairs = DiamondInspectFacet(diamondAddress).functionFacetPairs(); + + // Further processing of ownerFacet and pairs... } + + // Example of calling getStorage if this contract were a facet itself and had access + // function getDiamondStorage() internal pure returns (DiamondInspectFacet.DiamondStorage memory) { + // return DiamondInspectFacet.getStorage(); + // } }`} --> @@ -144,19 +181,19 @@ contract DiamondUser { ## Best Practices -- Ensure this facet is added to the diamond during initialization. -- Access `functionFacetPairs` through the diamond proxy for introspection. -- Use `getStorage` internally within other facets when direct storage access is required. +- Call `facetAddress` and `functionFacetPairs` through the diamond proxy address. +- Use the returned information to understand facet routing and state distribution. +- Ensure the diamond is deployed with the `DiamondInspectFacet` registered to inspect its functions. ## Security Considerations -The `functionFacetPairs` function is a view function and does not pose direct security risks. The `getStorage` function is internal and should only be used by trusted facets. Input validation is handled by the diamond proxy's dispatch mechanism. Follow standard Solidity security practices. +This facet is primarily for inspection and does not directly modify state. Ensure that calls to `facetAddress` and `functionFacetPairs` are made against a trusted diamond proxy address. Standard Solidity security practices apply to any contract interacting with the diamond.
- + diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx index 13015a4c..e49971ff 100644 --- a/website/docs/library/diamond/DiamondLoupeFacet.mdx +++ b/website/docs/library/diamond/DiamondLoupeFacet.mdx @@ -1,7 +1,7 @@ --- sidebar_position: 4 title: "DiamondLoupeFacet" -description: "Query diamond facets and function selectors" +description: "Inspect diamond facets and their associated function selectors" gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondLoupeFacet.sol" --- @@ -21,18 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Query diamond facets and function selectors +Inspect diamond facets and their associated function selectors -- Exposes external view functions for diamond introspection. -- Provides access to facet addresses and their supported selectors. +- Provides external functions for diamond introspection. +- Self-contained with no external dependencies. +- Follows Compose readability-first conventions. - Compatible with ERC-2535 diamond standard. ## Overview -This facet provides essential introspection capabilities for a diamond, allowing developers to query facet addresses, associated function selectors, and the overall facet deployment. It routes these queries through the diamond proxy, enabling external access to the diamond's internal structure. +This facet provides introspection capabilities for a diamond, allowing developers to query facet addresses and their supported function selectors. It routes calls through the diamond proxy to access shared storage. Add this facet to enable runtime inspection of diamond composition and upgradeability. --- @@ -77,7 +78,7 @@ This facet provides essential introspection capabilities for a diamond, allowing { name: "DIAMOND_STORAGE_POSITION", type: "bytes32", - description: "Diamond storage slot position for this module (Value: `keccak256(\"compose.diamond\")`)" + description: "Diamond storage slot position for this module (Value: `keccak256(\"erc8109.diamond\")`)" } ]} showRequired={false} @@ -85,41 +86,6 @@ This facet provides essential introspection capabilities for a diamond, allowing ## Functions -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- ### facetFunctionSelectors Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) β€” i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. @@ -205,29 +171,29 @@ Gets all facets and their selectors. Returns each unique facet address currently {`pragma solidity >=0.8.30; -import { IDiamond } from "@compose/diamond/IDiamond"; -import { Facet } from "@compose/diamond/DiamondLoupeFacet"; +import { IDiamond } from "@compose/diamond/Diamond.sol"; +import { DiamondLoupeFacet } from "@compose/diamond/DiamondLoupeFacet"; -contract DiamondConsumer { +contract DiamondUser { address immutable diamondAddress; constructor(address _diamondAddress) { diamondAddress = _diamondAddress; } - function getDiamondFacets() public view returns (Facet[] memory) { - IDiamond diamond = IDiamond(diamondAddress); - return diamond.facets(); + function getFacetAddresses() external view returns (address[]) { + // Calls facetAddresses through the diamond proxy + return IDiamond(diamondAddress).facetAddresses(); } - function getFacetAddress(bytes4 _selector) public view returns (address) { - IDiamond diamond = IDiamond(diamondAddress); - return diamond.facetAddress(_selector); + function getFacetSelectors(address _facet) external view returns (bytes4[]) { + // Calls facetFunctionSelectors through the diamond proxy + return IDiamond(diamondAddress).facetFunctionSelectors(_facet); } - function getFacetSelectors(address _facet) public view returns (bytes4[] memory) { - IDiamond diamond = IDiamond(diamondAddress); - return diamond.facetFunctionSelectors(_facet); + function getAllFacets() external view returns (DiamondLoupeFacet.Facet[] memory) { + // Calls facets through the diamond proxy + return IDiamond(diamondAddress).facets(); } }`} @@ -236,15 +202,15 @@ contract DiamondConsumer { ## Best Practices -- Call facet introspection functions through the diamond proxy to ensure consistent routing. -- Use `facets()` to get an overview of all deployed facets and their selectors. -- Utilize `facetAddress()` to determine which facet handles a specific function selector. +- Deploy this facet as part of the initial diamond setup. +- Use the external `facets()`, `facetAddresses()`, and `facetFunctionSelectors()` functions to understand diamond composition. +- Do not remove this facet if runtime inspection is required. ## Security Considerations -All functions in this facet are view functions and do not modify state. Follow standard Solidity security practices for contract interactions. +This facet is read-only and does not modify state, thus posing minimal security risks. Standard Solidity security practices apply to the diamond proxy itself.
@@ -255,24 +221,6 @@ All functions in this facet are view functions and do not modify state. Follow s href: "/docs/library/diamond/DiamondInspectFacet", description: "Related facet in diamond", icon: "πŸ’Ž" - }, - { - title: "ExampleDiamond", - href: "/docs/library/diamond/example/ExampleDiamond", - description: "Related facet in diamond", - icon: "πŸ’Ž" - }, - { - title: "DiamondCutFacet", - href: "/docs/library/diamond/DiamondCutFacet", - description: "Related facet in diamond", - icon: "πŸ’Ž" - }, - { - title: "DiamondCutFacet", - href: "/docs/library/diamond/DiamondCutFacet", - description: "Required for adding facets to diamonds", - icon: "πŸ”§" } ]} /> @@ -282,4 +230,4 @@ All functions in this facet are view functions and do not modify state. Follow s
- + diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx index 1193af8c..74a2e4b9 100644 --- a/website/docs/library/diamond/DiamondMod.mdx +++ b/website/docs/library/diamond/DiamondMod.mdx @@ -1,7 +1,7 @@ --- sidebar_position: 1 title: "DiamondMod" -description: "Manages diamond facets and function dispatch" +description: "Internal functions and storage for diamond proxy" gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondMod.sol" --- @@ -21,14 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages diamond facets and function dispatch +Internal functions and storage for diamond proxy -- Internal functions for facet management and call dispatch. -- Utilizes EIP-8042 diamond storage pattern. -- Supports adding facets during initial diamond deployment. -- Provides a fallback mechanism for function routing. +- Provides internal functions for facet management within a diamond. +- Supports the diamond storage pattern (EIP-8042) for shared state. +- `addFacets` is intended for initial diamond deployment only. +- `diamondFallback` enables dynamic function dispatch to registered facets. @@ -37,20 +37,15 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides core functionality for managing facets within a diamond proxy. It exposes internal functions for adding facets during deployment and a fallback mechanism for dispatching calls to the appropriate facet. This approach enables composability and upgradeability by allowing facets to be added and removed without altering the diamond's address. +This module provides internal functions for managing diamond facets and their associated function selectors. It facilitates the diamond storage pattern, allowing all facets to access and modify shared storage. Changes to the diamond's facet registry are immediately visible to all facets interacting with the diamond. --- ## Storage -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- ### DiamondStorage -storage-location: erc8042:compose.diamond +storage-location: erc8042:erc8109.diamond {`struct DiamondStorage { @@ -73,13 +68,12 @@ storage-location: erc8042:compose.diamond --- -### FacetCut +### FacetFunctions -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; +{`struct FacetFunctions { + address facet; + bytes4[] selectors; }`} @@ -90,7 +84,7 @@ storage-location: erc8042:compose.diamond { name: "DIAMOND_STORAGE_POSITION", type: "bytes32", - description: "Diamond storage slot position for this module (Value: `keccak256(\"compose.diamond\")`)" + description: "Diamond storage slot position for this module (Value: `keccak256(\"erc8109.diamond\")`)" } ]} showRequired={false} @@ -103,7 +97,7 @@ storage-location: erc8042:compose.diamond Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. -{`function addFacets(FacetCut[] memory _facets) ;`} +{`function addFacets(FacetFunctions[] memory _facets) ;`} **Parameters:** @@ -112,7 +106,7 @@ Adds facets and their function selectors to the diamond. Only supports adding fu properties={[ { name: "_facets", - type: "FacetCut[]", + type: "FacetFunctions[]", description: "" } ]} @@ -138,15 +132,36 @@ Find facet for function that is called and execute the function if a facet is fo ## Events - + +
+ Emitted when a function is added to a diamond. +
Signature: -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} +{`event DiamondFunctionAdded(bytes4 indexed _selector, address indexed _facet);`}
+
+ Parameters: + +
@@ -171,21 +186,12 @@ error FunctionNotFound(bytes4 _selector); - - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
Signature: -error NoBytecodeAtAddress(address _contractAddress, string _message); +error NoBytecodeAtAddress(address _contractAddress);
@@ -197,43 +203,41 @@ error NoBytecodeAtAddress(address _contractAddress, string _message); {`pragma solidity >=0.8.30; - -import "@compose/diamond/DiamondMod"; +import @compose/diamond/DiamondMod; contract MyFacet { - using DiamondMod for DiamondStorage; + // Assume DiamondMod is deployed at address diamondModAddress + DiamondMod internal diamondMod; - // Assume DiamondStorage is accessible and initialized - DiamondStorage internal diamondStorage; + constructor(address diamondModAddress) { + diamondMod = DiamondMod(diamondModAddress); + } /** - * @notice Example of calling addFacets during diamond initialization. - * @dev This function would typically be part of the diamond deployment script. + * @notice Example of calling addFacets within a diamond deployment context. + * This function should typically be called only during initial diamond deployment. */ - function initializeDiamond() external { - // Example: Add a facet with a specific selector range - // This is a simplified representation; actual deployment involves more steps. - // diamondStorage.addFacets(...); + function initializeDiamond(address[] memory _facets, bytes4[] memory _selectors) external { + // In a real deployment, this would be part of a larger initialization process. + // For demonstration, we assume _selectors are correctly mapped to _facets. + // The actual implementation of addFacets would handle the mapping. + // This example call is illustrative and may require adaptation based on the full deployment script. + // diamondMod.addFacets(_facets, _selectors); } /** - * @notice Example of how diamondFallback might be implemented. - * @dev This is a conceptual example of how a diamond might dispatch calls. + * @notice Example of calling diamondFallback to find and execute a function. */ - function delegateCall(bytes calldata _calldata) external payable returns (bytes memory) { - // This function is conceptually how diamondFallback would work. - // Actual diamondFallback implementation is in DiamondMod. - // return diamondStorage.diamondFallback(_calldata); - revert FunctionNotFound(); // Placeholder + function delegateCallFunction(bytes memory _calldata) external returns (bytes memory) { + // diamondMod.diamondFallback(_calldata) + return ""; // Placeholder return } /** - * @notice Example of accessing diamond storage. - * @dev Use getStorage to retrieve the storage slot. + * @notice Example of calling getStorage. */ - function getDiamondStorage() external pure returns (DiamondStorage storage) { - // return diamondStorage.getStorage(); // Conceptual call - return diamondStorage; // Placeholder + function viewDiamondStorage() external pure returns (bytes32) { + return diamondMod.getStorage(); } }`} @@ -242,32 +246,19 @@ contract MyFacet { ## Best Practices -- Ensure `addFacets` is only called during initial diamond deployment. -- Handle `FunctionNotFound` errors when calling `diamondFallback`. -- Use `getStorage` to access and manage diamond storage state consistently. +- Call `addFacets` only during the initial deployment of the diamond to avoid collisions. +- Use `diamondFallback` to delegate calls to the appropriate facet for unhandled selectors. +- Verify the `DIAMOND_STORAGE_POSITION` value is correctly set and used by all facets. ## Integration Notes -This module interacts with diamond storage at the `DIAMOND_STORAGE_POSITION`, identified by `keccak256("compose.diamond")`. It utilizes the `DiamondStorage` struct to manage facet mappings and function selectors. Changes to facet configurations made through functions like `addFacets` are immediately reflected in the diamond's dispatch logic, affecting all facets that rely on this shared storage. +This module utilizes the diamond storage pattern by referencing `DIAMOND_STORAGE_POSITION` which is set to `keccak256("erc8109.diamond")`. The `DiamondStorage` struct, though empty in definition, represents the shared state managed by the diamond. All functions, particularly `addFacets` and `diamondFallback`, interact with this shared storage to maintain the diamond's facet registry. Changes made via `addFacets` are immediately reflected and accessible by all facets through the diamond proxy. -
- -
-
- + diff --git a/website/docs/library/diamond/DiamondUpgradeFacet.mdx b/website/docs/library/diamond/DiamondUpgradeFacet.mdx new file mode 100644 index 00000000..528a4863 --- /dev/null +++ b/website/docs/library/diamond/DiamondUpgradeFacet.mdx @@ -0,0 +1,517 @@ +--- +sidebar_position: 510 +title: "DiamondUpgradeFacet" +description: "Manage diamond facets and upgrade logic" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondUpgradeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and upgrade logic + + + +- Allows adding, replacing, and removing facet functions. +- Emits events for function changes and delegate calls. +- Supports setting diamond metadata. +- Includes error handling for common upgrade issues. + + +## Overview + +This facet provides functions to add, replace, and remove facets within a diamond proxy. It orchestrates upgrades by modifying the diamond's function selector mappings. Developers use this facet to manage the diamond's surface area and upgrade its functionality while maintaining state. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetFunctions + + +{`struct FacetFunctions { + address facet; + bytes4[] selectors; +}`} + + +### State Variables + + + +## Functions + +### upgradeDiamond + +--- +### Function Changes: + +--- +### DelegateCall: + +--- +### Metadata: + +If _tag is non-zero or if _metadata.length > 0 then the `DiamondMetadata` event is emitted. + + +{`function upgradeDiamond( + FacetFunctions[] calldata _addFunctions, + FacetFunctions[] calldata _replaceFunctions, + bytes4[] calldata _removeFunctions, + address _delegate, + bytes calldata _functionCall, + bytes32 _tag, + bytes calldata _metadata +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when a function is added to a diamond. +
+ +
+ Signature: + +{`event DiamondFunctionAdded(bytes4 indexed _selector, address indexed _facet);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when changing the facet that will handle calls to a function. +
+ +
+ Signature: + +{`event DiamondFunctionReplaced(bytes4 indexed _selector, address indexed _oldFacet, address indexed _newFacet);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a function is removed from a diamond. +
+ +
+ Signature: + +{`event DiamondFunctionRemoved(bytes4 indexed _selector, address indexed _oldFacet);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a diamond's constructor function or function from a facet makes a `delegatecall`. +
+ +
+ Signature: + +{`event DiamondDelegateCall(address indexed _delegate, bytes _functionCall);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted to record information about a diamond. This event records any arbitrary metadata. The format of `_tag` and `_data` are not specified by the standard. +
+ +
+ Signature: + +{`event DiamondMetadata(bytes32 indexed _tag, bytes _data);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error DelegateCallReverted(address _delegate, bytes _functionCall); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+
+ + + + +## Best Practices + + +- Initialize diamond storage correctly before adding facets. +- Use `addFunctions`, `replaceFunctions`, and `removeFunctions` to manage the diamond's executable functions. +- Ensure facet bytecode is deployed and accessible at the provided address before calling upgrade functions. +- Verify that facet functions do not conflict with immutable functions. + + +## Security Considerations + + +Access to upgrade functions is restricted to the diamond's owner via the `OwnerUnauthorizedAccount` error. The `Metadata` function emits an event for tracking metadata changes. Input validation is performed to prevent adding duplicate functions or replacing non-existent ones. The `DelegateCall` function should be used with extreme caution due to reentrancy risks. + + +
+ +
+ +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondUpgradeMod.mdx b/website/docs/library/diamond/DiamondUpgradeMod.mdx new file mode 100644 index 00000000..3e68dff9 --- /dev/null +++ b/website/docs/library/diamond/DiamondUpgradeMod.mdx @@ -0,0 +1,586 @@ +--- +sidebar_position: 500 +title: "DiamondUpgradeMod" +description: "Upgrade diamond facets and manage function selectors" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondUpgradeMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Upgrade diamond facets and manage function selectors + + + +- Facet functions are `external` or `internal`, allowing integration into custom facets. +- Supports adding, replacing, and removing function selectors atomically. +- Emits events (`DiamondFunctionAdded`, `DiamondFunctionReplaced`, `DiamondFunctionRemoved`, `DiamondDelegateCall`, `DiamondMetadata`) to log upgrade activities. +- Enables state modification via `delegatecall` during upgrades. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides core functionality for upgrading diamond facets and managing function selectors. Facets import this module to add, replace, or remove functions, enabling dynamic updates to a diamond's capabilities. It ensures that changes are atomically applied and uses delegate calls for state modifications during upgrades. + +--- + +## Storage + +### DiamondStorage + +storage-location: erc8042:erc8109.diamond + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + +Data stored for each function selector Facet address of function selector Position of selector in the 'bytes4[] selectors' array + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### FacetFunctions + + +{`struct FacetFunctions { + address facet; + bytes4[] selectors; +}`} + + +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### upgradeDiamond + +Upgrade the diamond by adding, replacing, or removing functions. - `_addFunctions` maps new selectors to their facet implementations. - `_replaceFunctions` updates existing selectors to new facet addresses. - `_removeFunctions` removes selectors from the diamond. Functions added first, then replaced, then removed. These events are emitted to record changes to functions: - `DiamondFunctionAdded` - `DiamondFunctionReplaced` - `DiamondFunctionRemoved` If `_delegate` is non-zero, the diamond performs a `delegatecall` to `_delegate` using `_functionCall`. The `DiamondDelegateCall` event is emitted. The `delegatecall` is done to alter a diamond's state or to initialize, modify, or remove state after an upgrade. However, if `_delegate` is zero, no `delegatecall` is made and no `DiamondDelegateCall` event is emitted. If _tag is non-zero or if _metadata.length > 0 then the `DiamondMetadata` event is emitted. + + +{`function upgradeDiamond( +FacetFunctions[] calldata _addFunctions, +FacetFunctions[] calldata _replaceFunctions, +bytes4[] calldata _removeFunctions, +address _delegate, +bytes calldata _functionCall, +bytes32 _tag, +bytes calldata _metadata +) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when a diamond's constructor function or function from a facet makes a `delegatecall`. +
+ +
+ Signature: + +{`event DiamondDelegateCall(address indexed _delegate, bytes _functionCall);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a function is added to a diamond. +
+ +
+ Signature: + +{`event DiamondFunctionAdded(bytes4 indexed _selector, address indexed _facet);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a function is removed from a diamond. +
+ +
+ Signature: + +{`event DiamondFunctionRemoved(bytes4 indexed _selector, address indexed _oldFacet);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when changing the facet that will handle calls to a function. +
+ +
+ Signature: + +{`event DiamondFunctionReplaced(bytes4 indexed _selector, address indexed _oldFacet, address indexed _newFacet);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted to record information about a diamond. This event records any arbitrary metadata. The format of `_tag` and `_data` are not specified by the standard. +
+ +
+ Signature: + +{`event DiamondMetadata(bytes32 indexed _tag, bytes _data);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error DelegateCallReverted(address _delegate, bytes _functionCall); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress); + +
+
+ +
+ The functions below detect and revert with the following errors. +
+ +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+
+ + + + +## Best Practices + + +- Ensure that only authorized entities call `upgradeDiamond` to prevent unauthorized changes. +- Verify that function selectors and facet addresses are correctly specified before initiating an upgrade. +- Handle potential `DelegateCallReverted` errors when a `delegatecall` is performed as part of the upgrade process. + + +## Integration Notes + + +This module interacts with diamond storage via the `DIAMOND_STORAGE_POSITION` using the `keccak256("erc8109.diamond")` value. The `DiamondStorage` struct, though empty in the provided definition, is the conceptual root for diamond state. Functions like `addFunctions`, `removeFunctions`, `replaceFunctions`, and `upgradeDiamond` directly manipulate the diamond's function selector mappings stored within the diamond's storage. Changes are immediately visible to all facets interacting with the diamond. + + +
+ +
+ +
+ +
+ + diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx index abc7d675..803b309b 100644 --- a/website/docs/library/diamond/example/ExampleDiamond.mdx +++ b/website/docs/library/diamond/example/ExampleDiamond.mdx @@ -1,7 +1,7 @@ --- sidebar_position: 510 title: "ExampleDiamond" -description: "Example Diamond initialization and fallback handlers" +description: "Example Diamond library for Compose diamonds" gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/example/ExampleDiamond.sol" --- @@ -21,19 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Example Diamond initialization and fallback handlers +Example Diamond library for Compose diamonds -- Provides a constructor for diamond initialization. -- Includes fallback and receive functions for general contract interaction. -- Demonstrates facet cut structure for diamond assembly. -- Acts as a foundational example for Compose diamond deployments. +- Demonstrates diamond initialization with facets and owner. +- Exposes fallback and receive functions for ether handling. +- Provides a template for custom diamond implementations. +- Compliant with EIP-2535 Diamond Standard. ## Overview -This contract serves as an example for initializing a diamond with facets and owner. It also includes fallback and receive functions for handling arbitrary calls and ether transfers. Developers can reference this contract for structuring diamond deployments and understanding basic diamond proxy behavior. +This library provides an example implementation of a diamond contract, demonstrating facet registration and basic diamond functionality. It serves as a foundational template for developers to build upon. The example diamond initializes with provided facets and an owner, enabling routing of calls to registered facet functions. --- @@ -43,10 +43,10 @@ This contract serves as an example for initializing a diamond with facets and ow ### constructor -Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. +Struct to hold facet address and its function selectors. struct FacetFunctions { address facet; bytes4[] selectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} +{`constructor(DiamondMod.FacetFunctions[] memory _facets, address _diamondOwner) ;`} **Parameters:** @@ -55,7 +55,7 @@ Struct to hold facet address and its function selectors. struct FacetCut { properties={[ { name: "_facets", - type: "DiamondMod.FacetCut[]", + type: "DiamondMod.FacetFunctions[]", description: "Array of facet addresses and their corresponding function selectors to add to the diamond." }, { @@ -88,30 +88,24 @@ Struct to hold facet address and its function selectors. struct FacetCut { {`pragma solidity >=0.8.30; -import { IDiamond, DiamondMod } from '@compose/diamond/example/ExampleDiamond'; +import { ExampleDiamond, DiamondMod } from "@compose/diamond/example/ExampleDiamond"; -// Example: Deploying and initializing an ExampleDiamond -contract DeployExampleDiamond { +contract Deployer { address public diamondAddress; - constructor( - DiamondMod.FacetCut[] memory _facets, - address _diamondOwner - ) { - // Deploy and initialize the diamond - diamondAddress = address(new ExampleDiamond(_facets, _diamondOwner)); + function deployDiamond(address _diamondOwner, DiamondMod.FacetFunctions[] memory _facets) public { + ExampleDiamond exampleDiamond = new ExampleDiamond(_facets, _diamondOwner); + diamondAddress = address(exampleDiamond); } - // Example of calling a function through the diamond proxy (assuming it's implemented by an added facet) - function callFacetFunction(address _diamondAddress, bytes memory _calldata) public { - IDiamond(_diamondAddress).diamondCut(_calldata); + // Example of interacting with a deployed diamond (assuming a facet is registered) + function callRegisteredFacet(address _diamondAddress, bytes memory _calldata) public { + IDiamond(_diamondAddress).fallback.value(0)(_calldata); } +} - // Example of sending ether to the diamond - function sendEtherToDiamond(address _diamondAddress) public payable { - (bool success, ) = _diamondAddress.call{value: msg.value}(""); - require(success, "Failed to send ether to diamond"); - } +interface IDiamond { + function fallback(bytes calldata _calldata) external payable; }`} --> @@ -120,14 +114,14 @@ contract DeployExampleDiamond { - Initialize the diamond with all necessary facets and the owner during deployment. -- Ensure facet cut data is correctly formatted with actions, facet addresses, and function selectors. -- Use the fallback and receive functions for handling arbitrary calls and ether transfers to the diamond proxy as intended. +- Ensure facet function selectors accurately map to registered facet addresses. +- Utilize the diamond's fallback and receive functions for ether handling if required. ## Security Considerations -The constructor should only be called once during deployment. The fallback and receive functions should be reviewed for intended behavior, especially if they are intended to forward calls or handle value. Ensure that added facets implement robust access control for sensitive operations. +Follow standard Solidity security practices. Ensure that facet registration and initialization are performed securely. The fallback and receive functions should be carefully considered for ether handling to prevent unexpected behavior.
@@ -144,6 +138,18 @@ The constructor should only be called once during deployment. The fallback and r href: "/docs/library/diamond/DiamondInspectFacet", description: "Related facet in diamond", icon: "πŸ’Ž" + }, + { + title: "DiamondLoupeFacet", + href: "/docs/library/diamond/DiamondLoupeFacet", + description: "Related facet in diamond", + icon: "πŸ’Ž" + }, + { + title: "DiamondUpgradeFacet", + href: "/docs/library/diamond/DiamondUpgradeFacet", + description: "Related facet in diamond", + icon: "πŸ’Ž" } ]} /> @@ -153,4 +159,4 @@ The constructor should only be called once during deployment. The fallback and r
- + diff --git a/website/docs/library/diamond/example/index.mdx b/website/docs/library/diamond/example/index.mdx index 3ea3afe8..dc324eb7 100644 --- a/website/docs/library/diamond/example/index.mdx +++ b/website/docs/library/diamond/example/index.mdx @@ -14,7 +14,7 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" diff --git a/website/docs/library/diamond/index.mdx b/website/docs/library/diamond/index.mdx index 7ec8acb1..1bc7ba8b 100644 --- a/website/docs/library/diamond/index.mdx +++ b/website/docs/library/diamond/index.mdx @@ -35,23 +35,37 @@ import Icon from '@site/src/components/ui/Icon'; /> } size="medium" /> } size="medium" /> } size="medium" /> + } + size="medium" + /> + } + size="medium" + />