diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx
index 16e634a5..b1103d4f 100644
--- a/website/docs/library/access/AccessControl/AccessControlFacet.mdx
+++ b/website/docs/library/access/AccessControl/AccessControlFacet.mdx
@@ -26,13 +26,13 @@ Manages role-based access control within a diamond.
- Role-based access control for granular permission management.
-- Support for batch granting and revoking roles.
-- Extensible with other access control facets like `AccessControlPausableFacet`.
+- Supports hierarchical role administration, allowing roles to be managed by other roles.
+- Provides batch operations for granting and revoking roles to multiple accounts efficiently.
## Overview
-The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular permission management by defining roles and assigning them to accounts. This facet is crucial for securing sensitive functions and orchestrating complex interactions by enforcing role requirements.
+The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It enables granular permission management by defining roles, assigning them to accounts, and enforcing these assignments on function calls. This facet is essential for securing sensitive operations and ensuring that only authorized entities can perform specific actions.
---
@@ -477,49 +477,49 @@ error AccessControlUnauthorizedSender(address _sender, address _account);
{`pragma solidity ^0.8.30;
-import {DiamondInit} from "@compose/diamond/DiamondInit.sol";
-import {DiamondCut} from "@compose/diamond/DiamondCut.sol";
-import {AccessControlFacet} from "@compose/access/AccessControl/AccessControlFacet.sol";
-import {Diamond} from "@compose/diamond/Diamond.sol";
+import {DiamondCutFacet} from "@compose/diamond/DiamondCut/DiamondCutFacet";
+import {DiamondInitFacet} from "@compose/diamond/DiamondInit/DiamondInitFacet";
+import {DiamondLoupeFacet} from "@compose/diamond/DiamondLoupe/DiamondLoupeFacet";
+import {AccessControlFacet, AccessControlStorage} from "@compose/access/AccessControl/AccessControlFacet";
-contract DeployDiamondWithAccessControl is DiamondInit {
- // Define roles
- bytes32 constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
- bytes32 constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
+contract MyDiamond is DiamondInitFacet, DiamondCutFacet, DiamondLoupeFacet {
+ // ...
- function deploy() external {
- // ... diamond deployment logic ...
+ function upgrade() external {
+ // ...
+ AccessControlFacet accessControlFacet = AccessControlFacet(address(this));
+ bytes32 adminRole = keccak256("DIAMOND_ADMIN_ROLE");
+ bytes32 userRole = keccak256("USER_ROLE");
+ address owner = msg.sender;
- // Initialize AccessControlFacet
- AccessControlFacet accessControlFacet = AccessControlFacet(diamondAddress);
- accessControlFacet.grantRole(ADMIN_ROLE, msg.sender); // Grant admin role to deployer
- accessControlFacet.grantRole(OPERATOR_ROLE, address(this)); // Grant operator role to diamond itself
+ // Grant admin role to the owner
+ accessControlFacet.grantRole(adminRole, owner);
- // ... other facet initializations ...
- }
-
- // Example of calling a protected function using requireRole
- function executeProtectedAction() external {
- AccessControlFacet accessControlFacet = AccessControlFacet(diamondAddress);
- accessControlFacet.requireRole(OPERATOR_ROLE, msg.sender);
+ // Grant user role to a specific account
+ address userAccount = 0x123...;
+ accessControlFacet.grantRole(userRole, userAccount);
- // ... protected action logic ...
+ // Later, to check permissions or execute sensitive functions:
+ // accessControlFacet.requireRole(adminRole, msg.sender);
+ // bool hasPermission = accessControlFacet.hasRole(userRole, msg.sender);
}
+
+ // ...
}`}
## Best Practices
-- Initialize roles and grant them to appropriate accounts during diamond deployment.
-- Use `grantRoleBatch` and `revokeRoleBatch` for efficient mass role management.
-- Define clear hierarchies for roles using `setRoleAdmin` to manage administrative privileges.
+- Initialize AccessControlStorage at the correct storage slot (`keccak256("compose.accesscontrol")`) during diamond deployment.
+- Define clear and descriptive role names using `bytes32` constants for better readability and maintainability.
+- Leverage `grantRoleBatch` and `revokeRoleBatch` for efficient management of multiple accounts for a single role.
## Security Considerations
-Ensure that role administration is properly secured. The `setRoleAdmin`, `grantRole`, and `revokeRole` functions require the caller to be the admin of the role. Reentrancy is mitigated as role modifications are atomic. Input validation is handled internally by the facet to prevent invalid role or account assignments.
+Ensure that the `setRoleAdmin`, `grantRole`, and `revokeRole` functions are protected by appropriate access control mechanisms, typically by requiring the caller to be the admin of the role being modified. The `renounceRole` function allows accounts to revoke their own roles, which should be carefully considered in the overall access control strategy. Input validation on role names and account addresses is implicitly handled by the underlying Solidity types and Solidity's default behavior, but custom validation can be added if specific constraints are needed.
@@ -563,4 +563,4 @@ Ensure that role administration is properly secured. The `setRoleAdmin`, `grantR
-
+
diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx
index 6f97ba88..6dd00b4e 100644
--- a/website/docs/library/access/AccessControl/AccessControlMod.mdx
+++ b/website/docs/library/access/AccessControl/AccessControlMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "AccessControlMod"
-description: "Manages roles and permissions within a diamond."
+description: "Manages role-based access control for diamond functions."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControl/AccessControlMod.sol"
---
@@ -21,14 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages roles and permissions within a diamond.
+Manages role-based access control for diamond functions.
-- Permission management via roles assigned to accounts.
-- Ability to grant and revoke roles dynamically.
-- Built-in check for role existence with `hasRole`.
-- Revert mechanism for unauthorized access attempts via `requireRole`.
+- Role-based access control for granular permission management.
+- Functions to grant, revoke, and check role assignments.
+- Ability to define and manage administrative roles for other roles.
@@ -37,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-The AccessControl module provides a robust system for managing roles and permissions, ensuring that only authorized accounts can perform specific actions. This is crucial for maintaining security and control within a Compose diamond by enabling granular access delegation and revocation.
+The AccessControl module provides a robust system for managing permissions within your diamond. It allows you to define roles and grant them to specific addresses, ensuring that only authorized accounts can execute sensitive functions. This module is crucial for building secure and composable decentralized applications.
---
@@ -402,32 +401,26 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role);
{`pragma solidity ^0.8.30;
-import { AccessControlMod } from "@compose/access/AccessControl/AccessControlMod";
-import { AccessControlStorage } from "@compose/access/AccessControl/AccessControlStorage";
+import {IAccessControl} from "@compose/access/AccessControl/IAccessControl.sol";
+import {AccessControlStorage} from "@compose/access/AccessControl/AccessControlStorage.sol";
contract MyFacet {
- AccessControlMod internal accessControl;
+ IAccessControl accessControl;
- // Assuming AccessControlMod is initialized and its storage slot is known
- constructor(address _diamondProxy) {
- // Fetch storage location from the diamond proxy
- address accessControlAddress = _diamondProxy; // Example: If AccessControlMod is a facet itself
- accessControl = AccessControlMod(accessControlAddress);
+ constructor(address _diamondAddress) {
+ accessControl = IAccessControl(_diamondAddress);
}
function grantAdminRole(address _account) external {
- bytes32 adminRole = keccak256("AccessControl.ADMIN_ROLE"); // Example role
+ bytes32 adminRole = accessControl.getStorage().adminRole; // Assuming adminRole is accessible via storage
accessControl.grantRole(adminRole, _account);
}
- function checkAdminRole(address _account) view external returns (bool) {
- bytes32 adminRole = keccak256("AccessControl.ADMIN_ROLE"); // Example role
- return accessControl.hasRole(adminRole, _account);
- }
-
- function enforceAdminRole(address _account) view external {
- bytes32 adminRole = keccak256("AccessControl.ADMIN_ROLE"); // Example role
- accessControl.requireRole(adminRole, _account);
+ function checkAccess() public view {
+ address caller = msg.sender;
+ bytes32 someRole = keccak256("SOME_ROLE");
+ accessControl.requireRole(someRole, caller);
+ // ... proceed with function logic ...
}
}`}
@@ -435,15 +428,15 @@ contract MyFacet {
## Best Practices
-- Define roles using `bytes32` and `keccak256` for clarity and gas efficiency.
-- Use `requireRole` for immediate enforcement of permissions within functions.
-- Carefully manage the administration of roles using `setRoleAdmin` to prevent unintended privilege escalation.
+- Minimize the number of roles and grant them with the least privilege necessary.
+- Use descriptive `bytes32` role identifiers for clarity.
+- Ensure that role administration is properly secured, potentially using a dedicated admin role.
## Integration Notes
-The AccessControl module utilizes the diamond storage pattern, storing its state at a well-defined slot identified by `keccak256("compose.accesscontrol")`. Facets can access this state by calling the `getStorage()` function or directly interacting with the module's functions, which implicitly read from this storage slot. Ensure that the AccessControl module is correctly initialized and its storage slot is reserved to avoid conflicts with other modules.
+The AccessControl module utilizes a dedicated storage slot identified by `keccak256("compose.accesscontrol")` for its `AccessControlStorage`. Facets interacting with this module should import the `IAccessControl` interface and cast the diamond proxy address to it. The `getStorage()` function can be used to retrieve the storage struct, allowing read-only access to configuration like the admin role.
@@ -481,4 +474,4 @@ The AccessControl module utilizes the diamond storage pattern, storing its state
-
+
diff --git a/website/docs/library/access/AccessControl/index.mdx b/website/docs/library/access/AccessControl/index.mdx
index 64090982..5e0c2fba 100644
--- a/website/docs/library/access/AccessControl/index.mdx
+++ b/website/docs/library/access/AccessControl/index.mdx
@@ -21,7 +21,7 @@ import Icon from '@site/src/components/ui/Icon';
/>
}
size="medium"
diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx
index 441e7b57..69d0a5be 100644
--- a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx
+++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "AccessControlPausableFacet"
-description: "Control role access and pause/unpause specific roles."
+description: "Role-based pausing and unpausing of access control."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlPausable/AccessControlPausableFacet.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Control role access and pause/unpause specific roles.
+Role-based pausing and unpausing of access control.
-- Allows pausing and unpausing of specific roles, preventing execution of role-bound functions.
-- Integrates seamlessly with existing AccessControl mechanisms.
-- Provides view functions to check the current paused status of any role.
+- Enables temporary disabling of specific roles to halt associated operations.
+- Integrates seamlessly with existing access control mechanisms.
+- Emits `RolePaused` and `RoleUnpaused` events for on-chain monitoring and off-chain reactions.
## Overview
-This facet provides granular control over role-based access, allowing specific roles to be temporarily paused. It integrates with the core AccessControl logic to enforce role permissions and adds a pausing mechanism for enhanced operational flexibility. Use this facet to manage temporary disruptions or maintenance periods for specific functionalities tied to roles.
+This facet provides granular control over role execution within the diamond by allowing specific roles to be temporarily paused. It integrates with the underlying access control system to enforce pausing, ensuring critical functions are only callable when active. This enhances security and operational flexibility by enabling controlled downtime for specific role functionalities.
---
@@ -282,59 +282,53 @@ error AccessControlRolePaused(bytes32 _role);
{`pragma solidity ^0.8.30;
-import { Diamond } from "@compose/diamond/Diamond";
-import { AccessControlPausableFacet } from "@compose/access/AccessControlPausable/AccessControlPausableFacet";
-import { AccessControlFacet } from "@compose/access/AccessControl/AccessControlFacet";
+import { Diamond } from "@compose/diamond/Diamond.sol";
+import { AccessControlPausableFacet } from "@compose/access/AccessControlPausable/AccessControlPausableFacet.sol";
+import { AccessControlFacet } from "@compose/access/AccessControl/AccessControlFacet.sol";
+import { Role } from "@compose/access/AccessControl/Roles.sol";
contract MyDiamond is Diamond {
- constructor(address _diamondAdmin) Diamond( _diamondAdmin) {
- // ... deployment logic ...
- }
+ // ... deployment logic ...
- function upgrade() public {
- // Example: Adding AccessControlPausableFacet
- address[] memory facetsToAdd = new address[](1);
- facetsToAdd[0] = address(new AccessControlPausableFacet());
+ function deploy() external payable {
+ // ... other facet deployments ...
- bytes[] memory selectorsToAdd = new bytes[](3);
- selectorsToAdd[0] = AccessControlPausableFacet.pauseRole.selector;
- selectorsToAdd[1] = AccessControlPausableFacet.unpauseRole.selector;
- selectorsToAdd[2] = AccessControlPausableFacet.isRolePaused.selector;
+ address accessControlPausableFacet = deployFacet(AccessControlPausableFacet.contractId(), AccessControlPausableFacet);
+ address accessControlFacet = deployFacet(AccessControlFacet.contractId(), AccessControlFacet);
- facetCut(facetsToAdd, selectorsToAdd, new address[](0), new bytes[](0));
- }
+ // Grant roles and set up access control (example)
+ AccessControlFacet(accessControlFacet).grantRole(Role.ADMIN, msg.sender);
- function pauseMyRole() external {
- AccessControlPausableFacet pausableFacet = AccessControlPausableFacet(address(this));
- bytes32 myRole = keccak256("MY_ROLE");
- pausableFacet.pauseRole(myRole);
+ // Example: Pause a role
+ AccessControlPausableFacet(accessControlPausableFacet).pauseRole(Role.MAINTENANCE);
}
- function unpauseMyRole() external {
- AccessControlPausableFacet pausableFacet = AccessControlPausableFacet(address(this));
- bytes32 myRole = keccak256("MY_ROLE");
- pausableFacet.unpauseRole(myRole);
+ // Function to check if a role is paused
+ function isRolePaused(bytes32 _role) external view returns (bool) {
+ return AccessControlPausableFacet(getFacet(AccessControlPausableFacet.contractId())).isRolePaused(_role);
}
- function checkRoleStatus(bytes32 _role) external view returns (bool) {
- AccessControlPausableFacet pausableFacet = AccessControlPausableFacet(address(this));
- return pausableFacet.isRolePaused(_role);
+ // Function to require a role is not paused
+ function requireRoleNotPaused(bytes32 _role, address _account) external view {
+ AccessControlPausableFacet(getFacet(AccessControlPausableFacet.contractId())).requireRoleNotPaused(_role, _account);
}
+
+ // ... other diamond functions ...
}`}
## Best Practices
-- Initialize or upgrade the diamond to include this facet to enable role pausing capabilities.
-- Ensure the caller invoking `pauseRole` and `unpauseRole` has the necessary administrative privileges for the target role.
-- Leverage `requireRoleNotPaused` within other facets or contract logic to dynamically enforce pausing states.
+- Initialize roles and their pausing status during diamond deployment or through administrative functions.
+- Ensure the caller has the necessary permissions (typically role admin) to pause or unpause roles.
+- Leverage `requireRoleNotPaused` within other facets to enforce active status for critical operations tied to specific roles.
## Security Considerations
-The `pauseRole` and `unpauseRole` functions are restricted to the admin of the respective role, preventing unauthorized pausing. The `requireRoleNotPaused` function reverts with `AccessControlRolePaused` if the role is paused, ensuring that paused roles cannot be utilized. Ensure that any critical functions protected by roles properly call `requireRoleNotPaused` or equivalent logic to respect the paused state.
+The `pauseRole` and `unpauseRole` functions are protected by access control, ensuring only authorized administrators can modify the pausing state of a role. The `requireRoleNotPaused` function reverts with `AccessControlRolePaused` if the specified role is currently paused, preventing unauthorized execution. Reentrancy is not a concern as these functions only modify internal state or perform checks without external calls.
@@ -360,4 +354,4 @@ The `pauseRole` and `unpauseRole` functions are restricted to the admin of the r
-
+
diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx
index 894d4d00..54e0c85e 100644
--- a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx
+++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "AccessControlPausableMod"
-description: "Control role execution based on pause state."
+description: "Manage role-based pausing for diamond functionality."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlPausable/AccessControlPausableMod.sol"
---
@@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Control role execution based on pause state.
+Manage role-based pausing for diamond functionality.
-- Allows roles to be individually paused and unpaused.
-- Provides `requireRoleNotPaused` to enforce state checks before executing sensitive operations.
-- Emits `RolePaused` and `RoleUnpaused` events for state change tracking.
+- Role-specific pausing and unpausing mechanism.
+- Integrates with existing access control logic.
+- Provides a `requireRoleNotPaused` check for conditional execution.
@@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides fine-grained control over role execution by allowing roles to be paused. It integrates with the Access Control facet, enabling developers to conditionally block or allow actions associated with specific roles. This is crucial for emergency stops or scheduled maintenance, ensuring system safety and predictability.
+This module provides granular control over role-based pausing, allowing specific roles to be temporarily suspended. It integrates with the diamond's storage pattern and ensures that access control checks consider the paused state of roles, enhancing the safety and upgradeability of your diamond.
---
@@ -331,30 +331,29 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role);
{`pragma solidity ^0.8.30;
import {IAccessControlPausable} from "@compose/access/AccessControlPausable/IAccessControlPausable.sol";
+import {AccessControlPausableMod} from "@compose/access/AccessControlPausable/AccessControlPausableMod.sol";
contract MyFacet {
- IAccessControlPausable internal accessControlPausable;
+ IAccessControlPausable internal accessControlPausableFacet;
- constructor(address _diamondAddress) {
- accessControlPausable = IAccessControlPausable(_diamondAddress);
+ function initialize(address _diamondAddresses) public {
+ accessControlPausableFacet = IAccessControlPausable(_diamondAddresses);
}
- function _someRoleRestrictedAction() internal view {
- bytes32 MY_ROLE = keccak256("MY_ROLE");
- // Ensure the caller has MY_ROLE and that MY_ROLE is not paused.
- accessControlPausable.requireRoleNotPaused(MY_ROLE, msg.sender);
-
- // ... proceed with the action ...
+ function executeSensitiveAction(address _user) public {
+ bytes32 role = keccak256("MY_ROLE");
+ accessControlPausableFacet.requireRoleNotPaused(role, _user);
+ // ... proceed with action
}
- function pauseMyRole() external {
- bytes32 MY_ROLE = keccak256("MY_ROLE");
- accessControlPausable.pauseRole(MY_ROLE);
+ function pauseMyRole() public {
+ bytes32 role = keccak256("MY_ROLE");
+ accessControlPausableFacet.pauseRole(role);
}
- function unpauseMyRole() external {
- bytes32 MY_ROLE = keccak256("MY_ROLE");
- accessControlPausable.unpauseRole(MY_ROLE);
+ function unpauseMyRole() public {
+ bytes32 role = keccak256("MY_ROLE");
+ accessControlPausableFacet.unpauseRole(role);
}
}`}
@@ -362,15 +361,15 @@ contract MyFacet {
## Best Practices
-- Use `requireRoleNotPaused` to enforce that actions can only be performed when the associated role is not paused.
-- Monitor `RolePaused` and `RoleUnpaused` events to track changes in role states.
-- Integrate pausing functionality thoughtfully, considering emergency scenarios and system maintenance.
+- Always call `requireRoleNotPaused` before executing sensitive actions tied to a role.
+- Ensure roles are unpaused when no longer needed to restore functionality.
+- Handle `AccessControlRolePaused` errors gracefully in your calling facets.
## Integration Notes
-This module utilizes the diamond storage pattern, storing its state under the `ACCESS_CONTROL_STORAGE_POSITION` slot, identified by `keccak256("compose.accesscontrol")`. The `AccessControlPausableStorage` struct manages the pause state for roles. Facets interact with this module via the `IAccessControlPausable` interface to check and modify role pause states. The `requireRoleNotPaused` function also implicitly checks for role membership via the underlying Access Control logic.
+This module stores its state within the diamond's storage at the `ACCESS_CONTROL_STORAGE_POSITION` slot, identified by `keccak256("compose.accesscontrol")`. Facets that require role-based pausing functionality should import and interact with this module via its interface. The `AccessControlPausableStorage` struct is empty, indicating that pausing logic is managed externally or through events/internal state not exposed directly in this struct. The `AccessControlStorage` struct is also returned, suggesting an interaction or dependency with the base AccessControl module.
@@ -390,4 +389,4 @@ This module utilizes the diamond storage pattern, storing its state under the `A
-
+
diff --git a/website/docs/library/access/AccessControlPausable/index.mdx b/website/docs/library/access/AccessControlPausable/index.mdx
index 36b8298e..fe6c92e1 100644
--- a/website/docs/library/access/AccessControlPausable/index.mdx
+++ b/website/docs/library/access/AccessControlPausable/index.mdx
@@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx
index ea35e7ef..3c530776 100644
--- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx
+++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "AccessControlTemporalFacet"
-description: "Manages role assignments with time-based expiry."
+description: "Manages role assignments with time-based expirations."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages role assignments with time-based expiry.
+Manages role assignments with time-based expirations.
-- Grants roles with specific expiration timestamps.
-- Automatically revokes roles upon expiry.
-- Provides functions to check role expiry status.
+- Grants roles with specific expiry timestamps.
+- Automatically enforces role expiration, revoking access.
+- Provides functions to check role expiry status and enforce validity.
## Overview
-The AccessControlTemporalFacet extends the base access control mechanism by introducing time-bound role assignments. This allows for temporary privileges to be granted and automatically revoked upon expiration, enhancing granular control and security within a Compose diamond.
+The AccessControlTemporalFacet extends Compose's access control system by introducing time-bound role assignments. It allows for roles to be granted and automatically revoked upon expiration, enhancing dynamic permission management within a diamond.
---
@@ -359,32 +359,49 @@ error AccessControlRoleExpired(bytes32 _role, address _account);
{`pragma solidity ^0.8.30;
-import { DiamondLoupeFacet } from "@compose/diamond-loupe/DiamondLoupeFacet";
-import { Diamond } from "@compose/diamond-proxy/Diamond";
-import { AccessControlTemporalFacet } from "@compose/access/AccessControlTemporal/AccessControlTemporalFacet";
+import {DiamondCutFacet} from "@compose/diamond/DiamondCutFacet";
+import {AccessControlTemporalFacet} from "@compose/access/AccessControlTemporal/AccessControlTemporalFacet";
+import {DiamondInit} from "@compose/diamond/DiamondInit";
+import {DiamondProxy} from "@compose/diamond/DiamondProxy";
-contract Deployer {
- address public diamondProxy;
+contract DeployDiamond {
+ function deploy() external {
+ // ... deployment logic ...
- function deployDiamond() public {
- // Facet deployment logic here...
- address accessControlTemporalFacet = address(new AccessControlTemporalFacet());
+ AccessControlTemporalFacet accessControlTemporalFacet = new AccessControlTemporalFacet();
- // Diamond initialization logic here...
- // Assuming diamondProxy is already deployed and initialized with base facets
- // Add the AccessControlTemporalFacet
- // diamondProxy.diamondCut(...);
+ // Add facet to diamond cut
+ // diamondCut.addFacet(address(accessControlTemporalFacet), ...);
- // Grant a role with expiry
- bytes32 role = keccak256("TEMPORARY_ADMIN_ROLE");
- address user = 0x1234567890123456789012345678901234567890;
- uint256 expiry = block.timestamp + 3600; // Expires in 1 hour
+ // Initialize facet if needed
+ // diamondInit.init(address(accessControlTemporalFacet), ...);
+ }
+
+ function grantRole() external {
+ address diamondProxyAddress = /* address of your diamond proxy */ ;
+ AccessControlTemporalFacet accessControlTemporal = AccessControlTemporalFacet(diamondProxyAddress);
+
+ bytes32 role = keccak256("MY_TEMPORAL_ROLE");
+ address user = address(0x123);
+ uint256 expiryTimestamp = block.timestamp + 3600; // Expires in 1 hour
+
+ accessControlTemporal.grantRoleWithExpiry(role, user, expiryTimestamp);
+ }
+
+ function checkRole() external view {
+ address diamondProxyAddress = /* address of your diamond proxy */ ;
+ AccessControlTemporalFacet accessControlTemporal = AccessControlTemporalFacet(diamondProxyAddress);
+
+ bytes32 role = keccak256("MY_TEMPORAL_ROLE");
+ address user = address(0x123);
- AccessControlTemporalFacet(diamondProxy).grantRoleWithExpiry(role, user, expiry);
+ if (accessControlTemporal.isRoleExpired(role, user)) {
+ // Role has expired
+ } else {
+ // Role is valid
+ }
- // Check role status
- bool expired = AccessControlTemporalFacet(diamondProxy).isRoleExpired(role, user);
- uint256 expiryTime = AccessControlTemporalFacet(diamondProxy).getRoleExpiry(role, user);
+ accessControlTemporal.requireValidRole(role, user); // Reverts if invalid or expired
}
}`}
@@ -392,15 +409,15 @@ contract Deployer {
## Best Practices
-- Initialize this facet with appropriate roles and administrative permissions during diamond deployment.
-- Carefully consider the expiry durations for role grants to align with the principle of least privilege.
-- Integrate checks for role expiry using `isRoleExpired` or `requireValidRole` in functions that require temporally-limited access.
+- Initialize the facet with appropriate role admin configurations during diamond deployment.
+- Use `grantRoleWithExpiry` for roles that should have a limited duration.
+- Integrate `requireValidRole` checks in functions where temporal role validity is critical.
## Security Considerations
-Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the role's admin, preventing unauthorized temporal role management. Ensure that the underlying access control mechanism correctly enforces these admin roles. The `requireValidRole` function reverts with `AccessControlRoleExpired` if the role has expired, preventing the use of outdated privileges.
+Ensure that the caller granting or revoking temporal roles has the necessary administrative privileges to prevent unauthorized access modifications. Be mindful of potential race conditions if temporal role checks are not atomically handled with state-changing operations.
@@ -426,4 +443,4 @@ Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the ro
-
+
diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx
index fa21370a..2c3a5d1e 100644
--- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx
+++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "AccessControlTemporalMod"
-description: "Manages role assignments with expiry for diamond access control."
+description: "Manage role assignments with time-based expirations."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlTemporal/AccessControlTemporalMod.sol"
---
@@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages role assignments with expiry for diamond access control.
+Manage role assignments with time-based expirations.
-- Grants roles with a specific expiration timestamp.
-- Automatically enforces role validity, reverting if expired.
-- Explicitly revokes temporal roles before their expiry.
+- Roles can be granted with a specific `_expiresAt` timestamp.
+- `isRoleExpired` allows checking the validity of a role assignment.
+- `requireValidRole` provides a convenient, revert-based check for valid role assignments, including expiry.
@@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module introduces time-bound role assignments, enabling temporary permissions for accounts. By integrating with the diamond's access control, it enhances security by automatically revoking expired roles, reducing the need for manual cleanup and preventing stale permissions from being exploited.
+This module extends role-based access control by allowing roles to be granted with specific expiry timestamps. It ensures that access is automatically revoked once a role's validity period concludes, enhancing security and simplifying access management for temporary permissions.
---
@@ -436,19 +436,26 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role);
import {IAccessControlTemporal} from "@compose/access/AccessControlTemporal/IAccessControlTemporal.sol";
import {AccessControlTemporalMod} from "@compose/access/AccessControlTemporal/AccessControlTemporalMod.sol";
-contract MyFacet {
- IAccessControlTemporal internal constant accessControlTemporal = IAccessControlTemporal(AccessControlTemporalMod.ACCESS_CONTROL_STORAGE_POSITION);
+contract MyDiamondFacet {
+ AccessControlTemporalMod internal accessControlTemporalMod;
- function grantTemporaryRole(bytes32 _role, address _account, uint256 _expiresAt) external {
- accessControlTemporal.grantRoleWithExpiry(_role, _account, _expiresAt);
+ function initialize(address _diamondProxy) external {
+ accessControlTemporalMod = AccessControlTemporalMod(_diamondProxy);
}
- function checkRole(bytes32 _role, address _account) external view {
- accessControlTemporal.requireValidRole(_role, _account);
+ /**
+ * Grant a temporary role to an account.
+ */
+ function grantTempAdmin(address _account, uint256 _duration) external {
+ uint256 expiresAt = block.timestamp + _duration;
+ accessControlTemporalMod.grantRoleWithExpiry(AccessControlMod.ADMIN_ROLE, _account, expiresAt);
}
- function revokeRole(bytes32 _role, address _account) external {
- accessControlTemporal.revokeTemporalRole(_role, _account);
+ /**
+ * Check if an account has a valid, non-expired role.
+ */
+ function checkAdminRole(address _account) external view {
+ accessControlTemporalMod.requireValidRole(AccessControlMod.ADMIN_ROLE, _account);
}
}`}
@@ -456,15 +463,15 @@ contract MyFacet {
## Best Practices
-- Use `requireValidRole` to enforce temporal access control checks before sensitive operations, ensuring roles have not expired.
-- Grant roles with specific, short expiry durations to minimize the attack surface and adhere to the principle of least privilege.
-- Monitor `RoleGrantedWithExpiry` and `TemporalRoleRevoked` events for auditing and off-chain access control management.
+- Use `grantRoleWithExpiry` to assign temporary permissions, preventing stale access.
+- Leverage `requireValidRole` within your facets to enforce time-bound access control checks.
+- Consider the implications of overlapping or sequential temporary roles to avoid unintended access gaps or overlaps.
## Integration Notes
-AccessControlTemporalMod utilizes diamond storage at the `ACCESS_CONTROL_STORAGE_POSITION` (keccak256("compose.accesscontrol")) to manage temporal role assignments. Facets interacting with this module should use the `IAccessControlTemporal` interface. The module's state is managed independently of the base `AccessControlStorage` but is accessible through the diamond storage pattern. Ensure that the `AccessControlTemporalFacet` is correctly implemented and added to the diamond to expose these functions.
+AccessControlTemporalMod interacts with the diamond's storage using the `ACCESS_CONTROL_STORAGE_POSITION` slot, which is managed by the `AccessControlStorage` struct. Facets integrating with this module can call its functions directly after initialization. The module's state for temporal roles is distinct and managed internally, but the core access control checks (like role existence) might rely on the base `AccessControlStorage`.
@@ -496,4 +503,4 @@ AccessControlTemporalMod utilizes diamond storage at the `ACCESS_CONTROL_STORAGE
-
+
diff --git a/website/docs/library/access/AccessControlTemporal/index.mdx b/website/docs/library/access/AccessControlTemporal/index.mdx
index 792cc2db..7f94854c 100644
--- a/website/docs/library/access/AccessControlTemporal/index.mdx
+++ b/website/docs/library/access/AccessControlTemporal/index.mdx
@@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx
index 4c4e88b7..44c3490a 100644
--- a/website/docs/library/access/Owner/OwnerFacet.mdx
+++ b/website/docs/library/access/Owner/OwnerFacet.mdx
@@ -25,14 +25,13 @@ Manages contract ownership and transfers.
-- Provides `owner()` to view the current contract owner.
-- Enables `transferOwnership(address _newOwner)` to delegate control.
-- Supports `renounceOwnership()` to relinquish control, making the contract effectively immutable regarding ownership changes.
+- Clearly defines and enforces contract ownership.
+- Provides programmatic control over ownership transfers.
## Overview
-The OwnerFacet provides essential functionality for managing contract ownership within a Compose diamond. It allows querying the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is fundamental for access control and administrative operations.
+The OwnerFacet provides essential functionality for managing the ownership of a Compose diamond. It allows querying the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is fundamental for access control and administrative operations within the diamond.
---
@@ -145,51 +144,35 @@ error OwnerUnauthorizedAccount();
{`pragma solidity ^0.8.30;
-import {DiamondCutFacet} from "@compose/diamond/DiamondCutFacet";
-import {DiamondInit} from "@compose/diamond/DiamondInit";
-import {DiamondLoupeFacet} from "@compose/diamond/DiamondLoupeFacet";
-import {OwnerFacet, OwnerStorage} from "@compose/access/Owner/OwnerFacet";
-
-contract DeployOwnerDiamond is DiamondInit {
- function deploy() public {
- // ... deployment setup ...
-
- // Add OwnerFacet
- OwnerFacet ownerFacet = new OwnerFacet();
- bytes memory ownerFacetData = abi.encodeWithSelector(OwnerFacet.transferOwnership.selector, msg.sender); // Set initial owner to deployer
- IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
- cut[0] = IDiamondCut.FacetCut({
- facetAddress: address(ownerFacet),
- action: IDiamondCut.Action.Add,
- functionSelectors: DiamondCutFacet.getSelectors(ownerFacet)
- });
-
- // ... execute diamond cut ...
-
- // Call initializer function if needed (not applicable for OwnerFacet)
-
- // Verify owner
- OwnerFacet deployedOwnerFacet = OwnerFacet(diamondAddress);
- address currentOwner = deployedOwnerFacet.owner();
- // assert(currentOwner == msg.sender);
+import {DiamondProxy} from "@compose/core/DiamondProxy.sol";
+import {OwnerFacet, IOwnerFacet} from "@compose/access/Owner/OwnerFacet.sol";
+
+contract MyDiamond is DiamondProxy {
+ function owner() external view returns (address) {
+ return IOwnerFacet(address(this)).owner();
+ }
+
+ function transferOwnership(address _newOwner) external {
+ IOwnerFacet(address(this)).transferOwnership(_newOwner);
}
- // ... other deployment logic ...
+ function renounceOwnership() external {
+ IOwnerFacet(address(this)).renounceOwnership();
+ }
}`}
## Best Practices
-- Initialize ownership during diamond deployment, typically setting the deployer as the initial owner.
-- Treat ownership transfers with caution; consider using a multi-signature wallet or a timelock for critical transfers.
-- Ensure related access control facets (e.g., OwnerTwoStepsFacet) are integrated correctly if advanced ownership patterns are required.
+- Initialize ownership during diamond deployment. The initial owner is typically the deployer.
+- Use `transferOwnership` for planned ownership changes, and `renounceOwnership` only when ownership is no longer required.
## Security Considerations
-Access control is paramount. Only the current owner can call `transferOwnership` and `renounceOwnership`. The `OwnerUnauthorizedAccount` error is emitted if an unauthorized account attempts these actions. Transferring ownership to `address(0)` effectively renounces ownership, making subsequent ownership-dependent functions inaccessible unless ownership is re-established by another mechanism.
+Ownership changes must be carefully managed. Ensure that `transferOwnership` is called with the correct `_newOwner` address to prevent accidental loss of control or transfer to unauthorized accounts. The `OwnerUnauthorizedAccount` error is emitted if a non-owner attempts to call administrative functions. `transferOwnership` allows setting the new owner to `address(0)` to renounce ownership.
@@ -221,4 +204,4 @@ Access control is paramount. Only the current owner can call `transferOwnership`
-
+
diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx
index 85386393..50c62f34 100644
--- a/website/docs/library/access/Owner/OwnerMod.mdx
+++ b/website/docs/library/access/Owner/OwnerMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "OwnerMod"
-description: "Manages ERC-173 contract ownership and access control."
+description: "Manages ERC-173 contract ownership and owner transfers."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/Owner/OwnerMod.sol"
---
@@ -21,13 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages ERC-173 contract ownership and access control.
+Manages ERC-173 contract ownership and owner transfers.
- Implements ERC-173 standard for contract ownership.
-- Provides `owner()` and `requireOwner()` for access control.
-- Supports renouncing ownership by setting the owner to `address(0)`.
+- Provides `owner()` view function to retrieve the current owner's address.
+- Includes `requireOwner()` for access control, reverting if the caller is not the owner.
+- Supports ownership renouncement by transferring ownership to `address(0)`.
@@ -36,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-The OwnerMod provides essential ERC-173 ownership management functions. It defines the storage layout for the contract owner and offers utility functions like `owner()` and `requireOwner()` for access control. This module is fundamental for secure diamond upgrades and administrative operations.
+This module provides the core functionality for ERC-173 contract ownership, including retrieving the current owner and transferring ownership. It utilizes a dedicated storage slot for owner information, ensuring a clear separation of concerns within the diamond's storage layout.
---
@@ -210,47 +211,43 @@ error OwnerUnauthorizedAccount();
import {IOwnerMod, OwnerStorage} from "@compose/access/Owner/OwnerMod";
contract MyOwnerFacet {
- IOwnerMod public ownerMod;
+ IOwnerMod private immutable ownerMod;
- function initialize(address _owner) external {
- // Assuming OwnerMod is deployed and its address is known
- // In a real diamond, this would be handled by the Diamond initializer
- // For example purposes, we set it directly here.
- address ownerModAddress = address(0x1234567890123456789012345678901234567890;
+ constructor(address ownerModAddress) {
ownerMod = IOwnerMod(ownerModAddress);
- ownerMod.transferOwnership(_owner);
}
- function getOwner() external view returns (address) {
+ function getOwner() public view returns (address) {
return ownerMod.owner();
}
- function setOwner(address _newOwner) external {
- // Access control check, only owner can call this
- ownerMod.requireOwner();
+ function transferOwner(address _newOwner) public {
ownerMod.transferOwnership(_newOwner);
}
- function renounce() external {
- ownerMod.requireOwner();
- // Renounce ownership by setting owner to address(0)
+ function renounceOwnership() public {
ownerMod.transferOwnership(address(0));
}
+
+ function onlyOwnerFunction() public view {
+ ownerMod.requireOwner();
+ // ... function logic here ...
+ }
}`}
## Best Practices
-- Always use `requireOwner()` to protect sensitive administrative functions.
-- Be cautious when renouncing ownership by setting the owner to `address(0)`, as administrative functions will become inaccessible.
-- Ensure `OwnerMod` is initialized with a secure owner address during deployment.
+- Use `transferOwnership(address(0))` to renounce ownership, as indicated by the module's description.
+- Always check the `OwnerUnauthorizedAccount` error before proceeding with sensitive operations if the caller is not the owner.
+- Ensure the `OwnerMod` is initialized correctly during diamond deployment to prevent ownership-related issues.
## Integration Notes
-The `OwnerMod` utilizes a dedicated storage slot identified by `STORAGE_POSITION` (keccak256("compose.owner")) to store its `OwnerStorage` struct. This struct contains the `owner` address. Facets interacting with ownership functions must import and reference `IOwnerMod` and ensure the `OwnerMod` is correctly initialized and its address is discoverable within the diamond.
+The `OwnerMod` stores its state in a single storage slot identified by `STORAGE_POSITION` (keccak256("compose.owner")). The `OwnerStorage` struct contains the `owner` address. Facets interacting with this module should import `IOwnerMod` and use the provided addresses. The `getStorage()` function returns a pointer to this storage, allowing direct access if needed, though module functions are preferred for abstraction.
@@ -288,4 +285,4 @@ The `OwnerMod` utilizes a dedicated storage slot identified by `STORAGE_POSITION
-
+
diff --git a/website/docs/library/access/Owner/index.mdx b/website/docs/library/access/Owner/index.mdx
index 47d08d92..77c35d97 100644
--- a/website/docs/library/access/Owner/index.mdx
+++ b/website/docs/library/access/Owner/index.mdx
@@ -21,7 +21,7 @@ import Icon from '@site/src/components/ui/Icon';
/>
}
size="medium"
diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx
index 929145b9..b947f783 100644
--- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx
+++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx
@@ -25,14 +25,14 @@ Manage diamond ownership with a two-step transfer process.
-- Secure two-step ownership transfer process (`transferOwnership` and `acceptOwnership`).
-- Allows for ownership renouncement, making the contract ownerless.
-- Provides view functions (`owner`, `pendingOwner`) to check current and pending ownership.
+- Implements a two-step ownership transfer process for enhanced security.
+- Provides functions to query the current and pending owner addresses.
+- Allows for ownership renouncement by the current owner.
## Overview
-The OwnerTwoSteps facet provides a secure, two-step mechanism for transferring ownership of a Compose diamond. This pattern prevents accidental or malicious ownership changes by requiring both the current owner to initiate the transfer and the new owner to accept it, enhancing the diamond's security and operational integrity.
+The OwnerTwoSteps facet implements a secure, two-step ownership transfer mechanism for Compose diamonds. It prevents accidental ownership loss by requiring both the current owner to initiate a transfer and the new owner to accept it. This facet provides essential owner querying functions and manages the lifecycle of ownership changes.
---
@@ -198,33 +198,63 @@ error OwnerUnauthorizedAccount();
{`pragma solidity ^0.8.30;
-import {IOwnerTwoStepsFacet} from "@compose/access/OwnerTwoSteps/IOwnerTwoStepsFacet.sol";
+import {DiamondCutFacet} from "@compose/diamond/DiamondCutFacet.sol";
import {OwnerTwoStepsFacet} from "@compose/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol";
-// Assume diamond deployment and initialization context
+contract MyDiamond is DiamondCutFacet {
+ address internal constant OWNER_STORAGE_POSITION = keccak256(\"compose.owner\");
+ address internal constant PENDING_OWNER_STORAGE_POSITION = keccak256(\"compose.pendingOwner\");
-contract DiamondOwnerDeployer {
- address public diamondAddress;
+ struct OwnerStorage {
+ address owner;
+ }
+
+ struct PendingOwnerStorage {
+ address pendingOwner;
+ }
+
+ function getOwnerStorage() internal pure returns (OwnerStorage storage) {
+ assembly {
+ owner := sload(OWNER_STORAGE_POSITION)
+ }
+ }
- function deployDiamondWithOwner() external {
- // ... diamond deployment logic ...
- // diamondAddress = deployedDiamond;
+ function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage) {
+ assembly {
+ pendingOwner := sload(PENDING_OWNER_STORAGE_POSITION)
+ }
+ }
+
+ // ... other facets
- // Add OwnerTwoStepsFacet to the diamond
- // ... facet registration logic ...
+ function transferOwnership(address _newOwner) external {
+ address owner = this.owner();
+ require(msg.sender == owner, \"Ownable: caller is not the owner\");
+ getPendingOwnerStorage().pendingOwner = _newOwner;
+ emit OwnershipTransferStarted();
+ }
+
+ function owner() external view returns (address) {
+ return getOwnerStorage().owner;
+ }
- // Initialize the facet
- IOwnerTwoStepsFacet(diamondAddress).transferOwnership(msg.sender); // Or an admin address
+ function pendingOwner() external view returns (address) {
+ return getPendingOwnerStorage().pendingOwner;
}
- function acceptDiamondOwnership() external {
- // Call from the pending owner
- IOwnerTwoStepsFacet(diamondAddress).acceptOwnership();
+ function acceptOwnership() external {
+ address pending = this.pendingOwner();
+ require(msg.sender == pending, \"Ownable: caller is not the pending owner\");
+ getOwnerStorage().owner = pending;
+ delete getPendingOwnerStorage().pendingOwner;
+ emit OwnershipTransferred();
}
- function renounceDiamondOwnership() external {
- // Call from the current owner to renounce ownership
- IOwnerTwoStepsFacet(diamondAddress).renounceOwnership();
+ function renounceOwnership() external {
+ address owner = this.owner();
+ require(msg.sender == owner, \"Ownable: caller is not the owner\");
+ delete getOwnerStorage().owner;
+ emit OwnershipTransferred();
}
}`}
@@ -232,19 +262,19 @@ contract DiamondOwnerDeployer {
## Best Practices
-- Initialize ownership transfers using `transferOwnership` from an authorized address.
-- Ensure the intended new owner calls `acceptOwnership` to finalize the transfer.
-- Use `renounceOwnership` cautiously, as it makes the diamond contract unownable.
+- Initialize the diamond with the contract deployer as the initial owner.
+- Use `transferOwnership` followed by `acceptOwnership` for secure owner transitions.
+- Ensure the `OwnerTwoStepsFacet` is added to the diamond's facets during deployment or upgrades.
## Security Considerations
-Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. `acceptOwnership` can only be called by the address designated as the pending owner. Ensure that the address initiating `transferOwnership` is indeed the legitimate owner to prevent unauthorized ownership changes. Accidental renouncement will render the contract unmanageable by any address.
+The `transferOwnership` function is only callable by the current owner. The `acceptOwnership` function is only callable by the pending owner. The `renounceOwnership` function is only callable by the current owner. The `OwnerUnauthorizedAccount` error is emitted when an unauthorized account attempts an ownership-related action.
-
+
diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx
index 509bbf0e..26eb050c 100644
--- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx
+++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "OwnerTwoStepsMod"
-description: "Two-step contract ownership transfer logic."
+description: "Two-step contract ownership transfer with access control."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol"
---
@@ -21,14 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Two-step contract ownership transfer logic.
+Two-step contract ownership transfer with access control.
-- Secure two-step ownership transfer process.
-- Explicit `acceptOwnership` confirmation required from the new owner.
-- Provides `owner()`, `pendingOwner()`, and `renounceOwnership()` for state inspection and management.
-- Integrates with diamond storage using defined storage positions.
+- Secure two-step ownership transfer to prevent accidental lockouts.
+- Ability to renounce ownership, setting the owner to `address(0)`.
+- Provides view functions to check current and pending ownership status.
@@ -37,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides a robust, two-step ownership transfer mechanism, enhancing security by requiring explicit confirmation from the new owner. It integrates seamlessly with the Compose diamond storage pattern, ensuring ownership state is managed predictably and upgrade-safely.
+This module implements a secure two-step ownership transfer mechanism, preventing accidental lockouts. It provides functions to initiate a transfer, accept it, and renounce ownership entirely. This pattern is crucial for maintaining control over diamond upgrades and critical administrative functions.
---
@@ -254,28 +253,40 @@ error OwnerUnauthorizedAccount();
{`pragma solidity ^0.8.30;
import {OwnerTwoStepsMod} from "@compose/access/OwnerTwoSteps/OwnerTwoStepsMod";
-import {IOwnerTwoStepsFacet} from "@compose/access/OwnerTwoSteps/IOwnerTwoStepsFacet";
+import {OwnerStorage} from "@compose/access/OwnerTwoSteps/OwnerTwoStepsMod";
contract MyFacet {
- // Assume OwnerTwoStepsMod is initialized and its storage slot is known
- address public constant OWNER_STORAGE_POSITION = OwnerTwoStepsMod.OWNER_STORAGE_POSITION;
- address public constant PENDING_OWNER_STORAGE_POSITION = OwnerTwoStepsMod.PENDING_OWNER_STORAGE_POSITION;
-
- // Example of calling transferOwnership from a facet
- function initiateOwnershipTransfer(address _newOwner) external {
- // Assuming IOwnerTwoStepsFacet is implemented by the diamond proxy
- IOwnerTwoStepsFacet(msg.sender).transferOwnership(_newOwner);
+ address owner;
+
+ function initialize() external {
+ // Assume owner is set during deployment or upgrade
+ // For example, setting it to msg.sender initially
+ owner = msg.sender;
+ }
+
+ function transferControl(address _newOwner) external {
+ OwnerTwoStepsMod.transferOwnership(_newOwner);
+ }
+
+ function acceptControl() external {
+ OwnerTwoStepsMod.acceptOwnership();
+ }
+
+ function getCurrentOwner() external view returns (address) {
+ return OwnerTwoStepsMod.owner();
+ }
+
+ function getPendingOwner() external view returns (address) {
+ return OwnerTwoStepsMod.pendingOwner();
}
- // Example of calling acceptOwnership
- function finalizeOwnershipTransfer() external {
- IOwnerTwoStepsFacet(msg.sender).acceptOwnership();
+ function renounceControl() external {
+ OwnerTwoStepsMod.renounceOwnership();
}
- // Example of checking ownership
- function checkOwnership() external view {
- require(OwnerTwoStepsMod.owner() == msg.sender, "Not the owner");
- // ... owner-specific logic
+ function onlyOwnerRestrictedFunction() external {
+ OwnerTwoStepsMod.requireOwner();
+ // ... owner-only logic ...
}
}`}
@@ -283,15 +294,15 @@ contract MyFacet {
## Best Practices
-- Always use the `transferOwnership` function to initiate a transfer and `acceptOwnership` to finalize it. Never call `acceptOwnership` directly without a prior `transferOwnership` call.
-- Implement `requireOwner()` checks judiciously, ensuring critical administrative functions are protected.
-- Be aware that `renounceOwnership()` permanently relinquishes ownership, setting the owner to `address(0)`.
+- Always use `transferOwnership` to initiate a transfer, followed by `acceptOwnership` by the new owner.
+- Implement `requireOwner` checks on functions that require administrative privileges.
+- Handle `OwnerUnauthorizedAccount` and `OwnerAlreadyRenounced` errors gracefully.
## Integration Notes
-This module utilizes the diamond storage pattern, storing ownership and pending ownership states at `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` respectively. Facets interacting with ownership logic should reference these positions and the `OwnerStorage` and `PendingOwnerStorage` structs. The `owner()` and `pendingOwner()` functions provide view access to these states. Functions like `requireOwner()` will revert if the caller is not the current owner, enforcing access control based on the module's state.
+The `OwnerTwoStepsMod` utilizes the diamond storage pattern. Storage for the current owner is located at `OWNER_STORAGE_POSITION` and is managed by the `OwnerStorage` struct. The pending owner's state is managed separately and accessible via `getPendingOwnerStorage`. Facets interacting with this module should use the provided view functions (`owner`, `pendingOwner`) to determine ownership status and the `requireOwner` function for access control.
@@ -311,4 +322,4 @@ This module utilizes the diamond storage pattern, storing ownership and pending
-
+
diff --git a/website/docs/library/access/OwnerTwoSteps/index.mdx b/website/docs/library/access/OwnerTwoSteps/index.mdx
index 10beec74..b7f73ce0 100644
--- a/website/docs/library/access/OwnerTwoSteps/index.mdx
+++ b/website/docs/library/access/OwnerTwoSteps/index.mdx
@@ -21,7 +21,7 @@ import Icon from '@site/src/components/ui/Icon';
/>
}
size="medium"
diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx
index 178c09a9..cd7a8a45 100644
--- a/website/docs/library/diamond/DiamondCutFacet.mdx
+++ b/website/docs/library/diamond/DiamondCutFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 3
title: "DiamondCutFacet"
-description: "Manage diamond facets and upgrade diamond proxy"
+description: "Manages diamond facet upgrades and initialization."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondCutFacet.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manage diamond facets and upgrade diamond proxy
+Manages diamond facet upgrades and initialization.
-- Allows atomic addition, replacement, and removal of functions from the diamond proxy.
-- Supports executing an initialization function after performing facet changes.
-- Provides error handling for common upgrade-related issues like unauthorized access or invalid operations.
+- Allows adding, replacing, and removing facets from the diamond proxy.
+- Supports optional initialization execution via `delegatecall` during an upgrade.
+- Enforces access control, requiring the caller to be the diamond owner.
## Overview
-The DiamondCutFacet provides essential functions for managing the diamond proxy's facets. It allows authorized callers to add, replace, or remove functions, effectively upgrading or modifying the diamond's functionality. This facet is crucial for maintaining and evolving the diamond's surface area.
+The DiamondCutFacet provides the core functionality for upgrading and managing the facets of a Compose diamond. It allows for adding new facets, replacing existing ones, and removing facets, all while ensuring proper access control and optional initialization execution.
---
@@ -265,47 +265,64 @@ error InitializationFunctionReverted(address _initializationContractAddress, byt
{`pragma solidity ^0.8.30;
import {DiamondCutFacet} from "@compose/diamond/DiamondCutFacet.sol";
-import {DiamondInit} from "@compose/diamond/DiamondInit.sol";
-import {Selectors} from "@compose/diamond/Selectors.sol";
-
-contract MyDiamond is DiamondCutFacet, DiamondInit {
- constructor(address[] memory _diamondFacets, bytes[] memory _facetCuts, address _init, bytes memory _calldata) DiamondInit(_diamondFacets, _facetCuts, _init, _calldata) {}
-
- // Functions from DiamondCutFacet can be called externally after deployment
- // Example: Adding a new facet
- function upgradeDiamond(address _newFacetAddress, bytes[] memory _newSelectors) external onlyOwner { // Assuming onlyOwner modifier is implemented elsewhere
- FacetCut[] memory cut = new FacetCut[](1);
- cut[0] = FacetCut(FacetCutAction.Add, _newFacetAddress, _newSelectors);
- diamondCut(cut, address(0), "");
+import {DiamondInit} from "@compose/diamond/DiamondInit.sol"; // Example Init contract
+import {IDiamondCut} from "@compose/diamond/IDiamondCut.sol";
+
+contract MyDiamond is IDiamondCut {
+
+ address constant DIAMOND_INIT_ADDRESS = address(new DiamondInit()); // Example initialization contract
+
+ function upgradeDiamond() external {
+ // Define facet cuts for upgrade
+ DiamondCutFacet.FacetCut[] memory cuts = new DiamondCutFacet.FacetCut[](2);
+
+ // Example: Add a new facet
+ cuts[0] = DiamondCutFacet.FacetCut({
+ facetAddress: address(0x123), // Address of the new facet contract
+ action: DiamondCutFacet.FacetCutAction.ADD,
+ selectors: new bytes[](0) // Selectors will be populated by the facet itself or computed
+ });
+
+ // Example: Replace an existing facet
+ cuts[1] = DiamondCutFacet.FacetCut({
+ facetAddress: address(0x456), // Address of the new version of the facet
+ action: DiamondCutFacet.FacetCutAction.REPLACE,
+ selectors: new bytes[](0) // Selectors will be populated by the facet itself or computed
+ });
+
+ // Execute the diamond cut
+ // Note: The DiamondCutFacet must be deployed and accessible within the diamond proxy
+ // For deployment, this would typically be called via the DiamondInit contract.
+ // This example assumes the diamondCut function is already registered and callable.
+ // In a real deployment scenario, you would call this via the Diamond proxy itself.
+ // For demonstration purposes, we simulate the call:
+ // diamondCut(cuts, DIAMOND_INIT_ADDRESS, abi.encodeWithSignature("initMyDiamond()", _someArgs));
+
+ // In a real scenario, assuming DiamondCutFacet is already part of the diamond:
+ // DiamondCutFacet(address(this)).diamondCut(cuts, DIAMOND_INIT_ADDRESS, abi.encodeWithSignature("initMyDiamond()"));
}
- // Example: Replacing an existing facet's function
- function replaceFunction(address _facetAddress, bytes4 _selector, address _newFacetAddress) external onlyOwner { // Assuming onlyOwner modifier is implemented elsewhere
- FacetCut[] memory cut = new FacetCut[](1);
- cut[0] = FacetCut(FacetCutAction.Replace, _facetAddress, new bytes[](1));
- cut[0].selectors[0] = _selector; // This is a simplification, actual implementation requires selector mapping
- // In a real scenario, you'd provide the new selector and its facet address
- // For demonstration, this shows the intent of replacing a selector
- // The diamondCut function expects an array of selectors for a given facet address
- // A more accurate call would involve constructing the correct FacetCut struct
- // with the new facet address and its selectors.
+ // Implement the IDiamondCut interface functions if MyDiamond is the proxy itself
+ function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external override {
+ // Implementation would call the actual DiamondCutFacet logic
}
-}
-`}
+
+ // Other IDiamondCut functions would be implemented here as well.
+}`}
## Best Practices
-- Ensure that the `diamondCut` function is only callable by an authorized entity (e.g., an owner or a governance contract) to prevent unauthorized upgrades.
-- When adding or replacing facets, carefully verify that the function selectors do not conflict with existing functions, especially immutable ones.
-- Always provide an initialization function (`_init` and `_calldata`) when performing complex upgrades to ensure the diamond's state is consistent after the cut.
+- Ensure the `DiamondCutFacet` is the first facet added to a new diamond or is already established as the upgrade mechanism.
+- Carefully manage the `action` field in `FacetCut` to perform ADD, REPLACE, or REMOVE operations correctly. Use `DELETE` for removing facets.
+- When performing an upgrade, ensure the `_init` address and `_calldata` are correctly set if initialization or post-upgrade logic is required.
## Security Considerations
-Access to the `diamondCut` function must be strictly controlled to prevent malicious actors from altering the diamond's behavior. Ensure that any initialization function provided is thoroughly audited to prevent state corruption. Be mindful of potential reentrancy if the initialization function or facets being added/replaced interact with external contracts. Immutable functions cannot be replaced or removed.
+Access to the `diamondCut` function is restricted to the owner of the diamond, preventing unauthorized upgrades. Ensure that the addresses of new facets are verified and that their bytecode is valid before adding or replacing them. Initialization functions executed via `delegatecall` must be carefully audited for reentrancy and state manipulation vulnerabilities.
@@ -337,4 +354,4 @@ Access to the `diamondCut` function must be strictly controlled to prevent malic
-
+
diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx
index 45f537a7..7e172da4 100644
--- a/website/docs/library/diamond/DiamondCutMod.mdx
+++ b/website/docs/library/diamond/DiamondCutMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "DiamondCutMod"
-description: "Manage diamond facets and function selectors."
+description: "Manages diamond facet additions, replacements, and removals."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondCutMod.sol"
---
@@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manage diamond facets and function selectors.
+Manages diamond facet additions, replacements, and removals.
-- Supports atomic addition, replacement, and removal of facets.
-- Allows for an optional initialization function to be executed post-cut.
-- Enforces checks to prevent adding duplicate functions or removing non-existent ones.
+- Supports adding, replacing, and removing facets atomically.
+- Allows for an optional initialization function call during the diamond cut.
+- Enforces checks against immutable functions and duplicate function additions.
@@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-The DiamondCutMod provides essential functions for upgrading and managing the composition of your diamond. It allows for atomic addition, replacement, or removal of facets and their associated functions, ensuring a controlled and predictable upgrade path. This module is critical for maintaining the integrity and evolvability of your diamond architecture.
+The DiamondCutMod provides essential functions for upgrading a Compose diamond by adding, replacing, or removing facets. It ensures atomic upgrades and safe initialization, crucial for maintaining diamond integrity and composability.
---
@@ -335,31 +335,34 @@ error RemoveFacetAddressMustBeZeroAddress(address _facet);
{`pragma solidity ^0.8.30;
import {DiamondCutMod} from "@compose/diamond/DiamondCutMod";
-import {IDiamondCut} from "@compose/diamond/IDiamondCut";
-import {FacetCut, FacetCutAction} from "@compose/diamond/DiamondCutMod";
+import {IDiamondCut} from "@compose/diamond/interfaces/IDiamondCut";
+import {FacetCutAction} from "@compose/diamond/structs/FacetCutAction";
+import {FacetCut} from "@compose/diamond/structs/FacetCut";
contract MyDiamond is IDiamondCut {
- function diamondCut(FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) external override {
- // Implementation that calls DiamondCutMod.diamondCut
- }
+ address immutable DIAMOND_STORAGE_POSITION;
- // Other diamond functions...
-}
+ constructor(address _diamondStoragePosition) {
+ DIAMOND_STORAGE_POSITION = _diamondStoragePosition;
+ }
-contract FacetInstaller {
- function installNewFacet(address _diamondAddress, address _newFacetAddress, bytes[] memory _functionSelectors) public {
- // Assume DiamondCutMod is deployed and accessible
- DiamondCutMod diamondCutMod = DiamondCutMod(_diamondAddress); // Or use a specific DiamondCutMod address if separate
+ function upgradeDiamond(
+ FacetCut[] memory _diamondCut,
+ address _init,
+ bytes memory _calldata
+ ) external {
+ DiamondCutMod(DIAMOND_STORAGE_POSITION).diamondCut(_diamondCut, _init, _calldata);
+ }
+ // Example of adding a new facet
+ function addMyNewFacet(address _facetAddress, bytes4[] memory _selectors) external {
FacetCut[] memory cuts = new FacetCut[](1);
- cuts[0].facetAddress = _newFacetAddress;
- cuts[0].action = FacetCutAction.ADD;
- cuts[0].functionSelectors = _functionSelectors;
-
- // Note: In a real scenario, you would likely use the diamond's own diamondCut function
- // which internally calls DiamondCutMod logic.
- // This example directly calls a hypothetical DiamondCutMod contract for illustration.
- // diamondCutMod.diamondCut(cuts, address(0), ""); // This is a simplified illustration
+ cuts[0] = FacetCut({
+ facetAddress: _facetAddress,
+ action: FacetCutAction.ADD,
+ selectors: _selectors
+ });
+ upgradeDiamond(cuts, address(0), "");
}
}`}
@@ -367,19 +370,19 @@ contract FacetInstaller {
## Best Practices
-- Always use custom errors for revert conditions to improve clarity and gas efficiency.
-- Ensure that any initialization function passed to `diamondCut` is thoroughly tested and reverts with a descriptive error if it fails.
-- Understand the `FacetCutAction` enum and use it correctly to avoid unintended state changes.
+- Ensure all facet additions, replacements, and removals are performed atomically using `diamondCut` to maintain state consistency.
+- Carefully validate the `FacetCut` actions and selector mappings to prevent unintended function overrides or removals.
+- Handle `InitializationFunctionReverted` errors by ensuring the provided initialization calldata is correct and the initialization function is robust.
## Integration Notes
-The DiamondCutMod interacts with the diamond's storage at the `DIAMOND_STORAGE_POSITION` key. It modifies the `DiamondStorage` struct, which is crucial for mapping function selectors to facet addresses. Facets should be aware that the set of available function selectors and their corresponding facet addresses can change dynamically through calls to `diamondCut`. The order of operations within a single `diamondCut` transaction is important for maintaining consistency.
+This module interacts with the diamond's storage at `DIAMOND_STORAGE_POSITION`, which is expected to hold a `DiamondStorage` struct. The `diamondCut` function directly modifies the facet mappings within this storage. Facets should be aware that the selectors and facet addresses managed by this module are global to the diamond and affect all interacting facets.
-
+
diff --git a/website/docs/library/diamond/DiamondInspectFacet.mdx b/website/docs/library/diamond/DiamondInspectFacet.mdx
index 2bced394..445545e4 100644
--- a/website/docs/library/diamond/DiamondInspectFacet.mdx
+++ b/website/docs/library/diamond/DiamondInspectFacet.mdx
@@ -25,14 +25,14 @@ Inspect diamond storage and function mappings.
-- Provides read-only access to diamond's function-to-facet mappings.
-- Enables programmatic inspection of the diamond's storage layout.
-- Utilizes inline assembly for direct storage access, ensuring efficiency for critical inspection tasks.
+- Exposes diamond storage layout via `getStorage()` (internal).
+- Provides a comprehensive mapping of function selectors to facet addresses via `functionFacetPairs()`.
+- Designed for read-only inspection, enhancing security.
## Overview
-The DiamondInspectFacet provides read-only access to the diamond's internal state and function routing. It allows external querying of how function selectors map to specific facet addresses, facilitating dynamic interaction and debugging without altering diamond state.
+The DiamondInspectFacet provides read-only access to the diamond's internal state and function routing information. It allows developers to query the diamond's storage layout and understand how function selectors are mapped to specific facet addresses.
---
@@ -111,27 +111,26 @@ Returns an array of all function selectors and their corresponding facet address
{`pragma solidity ^0.8.30;
-import {DiamondInspectFacet} from "@compose/diamond/DiamondInspectFacet";
-import {IDiamondCut} from "@compose/diamond/IDiamondCut";
+import { DiamondInspectFacet } from "@compose/diamond/DiamondInspectFacet";
+import { IDiamondCut } from "@compose/diamond/IDiamondCut";
contract Deployer {
- address immutable diamond;
+ address immutable diamondAddress;
- constructor(address _diamond) {
- diamond = _diamond;
+ constructor(address _diamondAddress) {
+ diamondAddress = _diamondAddress;
}
- function inspectDiamond() external view {
- DiamondInspectFacet inspectFacet = DiamondInspectFacet(diamond);
-
- // Get all function selector to facet address mappings
- DiamondInspectFacet.FunctionFacetPair[] memory pairs = inspectFacet.functionFacetPairs();
+ function inspectStorage() external view returns (bytes memory) {
+ DiamondInspectFacet inspectFacet = DiamondInspectFacet(diamondAddress);
+ // Note: getStorage() is internal and cannot be called directly from here.
+ // This example demonstrates calling a public/external view function.
+ return abi.encode(inspectFacet.functionFacetPairs());
+ }
- // Example: Iterate and log mappings (requires a logging mechanism or event)
- for (uint i = 0; i < pairs.length; i++) {
- // Log pairs[i].functionSelector and pairs[i].facetAddress
- // (Actual logging would involve events or external calls)
- }
+ function getFunctionMappings() external view returns (DiamondInspectFacet.FunctionFacetPair[] memory) {
+ DiamondInspectFacet inspectFacet = DiamondInspectFacet(diamondAddress);
+ return inspectFacet.functionFacetPairs();
}
}`}
@@ -139,19 +138,19 @@ contract Deployer {
## Best Practices
-- Integrate this facet to enable external inspection of diamond's function routing.
-- Use the `functionFacetPairs` function to dynamically resolve function calls when the target facet is not known statically.
-- Access storage directly via `getStorage` only when absolutely necessary for deep inspection, as it bypasses standard function call interfaces.
+- Integrate this facet to provide visibility into diamond operations without granting write access.
+- Use `functionFacetPairs()` to dynamically discover facet implementations for a given function selector.
+- Rely on the `DIAMOND_STORAGE_POSITION` constant for direct storage access in internal diamond logic, as demonstrated by `getStorage()`.
## Security Considerations
-This facet is read-only and does not introduce reentrancy risks. Direct storage access via `getStorage` should be used with caution to avoid misinterpreting raw storage data. Ensure that the diamond's storage slot `DIAMOND_STORAGE_POSITION` is correctly set to `keccak256("compose.diamond")` to prevent accidental data corruption.
+This facet is designed for read-only operations and poses minimal direct security risks. Ensure that any logic consuming its output does not grant elevated privileges based solely on inspected state, as this could lead to vulnerabilities if the diamond's state is manipulated through other means.
-
+
diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx
index 5723fcab..23acde8d 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: "Inspect diamond facets and function selectors."
+description: "Inspect diamond facets and their functions."
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';
-Inspect diamond facets and function selectors.
+Inspect diamond facets and their functions.
-- Provides access to the diamond's facet registry and function selector mappings.
-- Optimized for efficient querying of facet information, even in large diamonds.
-- Enables dynamic discovery of diamond functionality.
+- Provides read-only access to the diamond's facet registry.
+- Enables querying of facet addresses by function selector.
+- Returns all facets and their function selectors.
+- Optimized for gas efficiency when querying large diamonds.
## Overview
-The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query facet addresses, associated function selectors, and lists of all facets and their selectors within the diamond. This facet is crucial for understanding the diamond's on-chain structure and for dynamic interactions.
+The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query facet addresses, associated function selectors, and the overall facet structure of the diamond. This facet is crucial for understanding the diamond's composition and for dynamic interaction.
---
@@ -203,30 +204,30 @@ Gets all facets and their selectors. Returns each unique facet address currently
{`pragma solidity ^0.8.30;
-import { IDiamondCut } from "@compose/diamond/DiamondCutFacet.sol";
-import { IDiamondLoupe } from "@compose/diamond/DiamondLoupeFacet.sol";
-import { ExampleDiamond } from "@compose/diamond/ExampleDiamond.sol";
+import {DiamondLoupeFacet} from "@compose/diamond/DiamondLoupeFacet";
+import {IDiamondCut} from "@compose/diamond/IDiamondCut";
+import {IDiamondLoupe} from "@compose/diamond/IDiamondLoupe";
-contract DeployDiamondExample {
- address immutable diamondAddress;
-
- constructor() {
- // Assume ExampleDiamond is deployed and returns the diamond address
- diamondAddress = address(new ExampleDiamond());
+contract ExampleDiamond is IDiamondCut, IDiamondLoupe {
+ constructor(address _diamondAdmin, address _init) {
+ // Deployment logic for the diamond
}
- function getFacetAddresses() public view returns (address[] memory) {
- IDiamondLoupe loupe = IDiamondLoupe(diamondAddress);
- return loupe.facetAddresses();
- }
+ // Implement IDiamondCut and IDiamondLoupe functions
- function getFacets() public view returns (IDiamondLoupe.Facet[] memory) {
- IDiamondLoupe loupe = IDiamondLoupe(diamondAddress);
+ function getFacets() external view returns (Facet[] memory) {
+ // Assuming DiamondLoupeFacet is deployed and accessible
+ DiamondLoupeFacet loupe = DiamondLoupeFacet(address(this));
return loupe.facets();
}
- function getFacetForSelector(bytes4 _selector) public view returns (address) {
- IDiamondLoupe loupe = IDiamondLoupe(diamondAddress);
+ function getFacetFunctionSelectors(address _facet) external view returns (bytes4[] memory) {
+ DiamondLoupeFacet loupe = DiamondLoupeFacet(address(this));
+ return loupe.facetFunctionSelectors(_facet);
+ }
+
+ function getFacetAddress(bytes4 _selector) external view returns (address) {
+ DiamondLoupeFacet loupe = DiamondLoupeFacet(address(this));
return loupe.facetAddress(_selector);
}
}`}
@@ -235,15 +236,15 @@ contract DeployDiamondExample {
## Best Practices
-- Integrate DiamondLoupeFacet into your diamond to enable introspection.
-- Use `facetAddresses()` and `facets()` to understand the diamond's current facet composition.
-- Query `facetAddress(selector)` to determine which facet handles a specific function call.
+- Integrate DiamondLoupeFacet into your diamond to enable essential introspection functionalities.
+- Use the `facets()` function to retrieve a comprehensive list of all facets and their associated function selectors.
+- Leverage `facetAddress()` to dynamically determine which facet handles a specific function selector.
## Security Considerations
-This facet is primarily for inspection and does not modify diamond state. Ensure that the diamond's access control mechanisms are correctly implemented in other facets to prevent unauthorized modifications.
+This facet is read-only and does not manage state changes, thus posing minimal direct security risks. However, ensure it is correctly integrated into the diamond's routing mechanism. The accuracy of its output is dependent on the integrity of the DiamondCutFacet operations.
@@ -281,4 +282,4 @@ This facet is primarily for inspection and does not modify diamond state. Ensure
-
+
diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx
index a79f8e67..a410fc79 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, function routing, and storage."
+description: "Internal Diamond proxy logic and storage"
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondMod.sol"
---
@@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages diamond facets, function routing, and storage.
+Internal Diamond proxy logic and storage
-- Manages facet additions and function selector registrations.
-- Provides a fallback mechanism for routing external calls to the correct facet.
-- Exposes diamond storage for inspection via `getStorage`.
+- Manages diamond proxy state via a single storage slot (`DIAMOND_STORAGE_POSITION`).
+- Provides internal functions for facet management and function dispatch.
+- Supports the Diamond Standard (EIP-2535) for modularity and upgradeability.
@@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-DiamondMod provides core diamond proxy logic, enabling dynamic facet composition and function dispatch. It facilitates adding new functionalities via facets and ensures that calls are correctly routed to the appropriate facet implementation, crucial for upgradeability and modularity in Compose diamonds.
+DiamondMod provides core internal functions and storage management for the Diamond proxy pattern. It handles facet registration and function dispatch, enabling modularity and upgradeability within a diamond architecture.
---
@@ -197,38 +197,34 @@ error NoBytecodeAtAddress(address _contractAddress, string _message);
import {DiamondMod} from "@compose/diamond/DiamondMod";
-contract MyDiamond {
- function upgradeDiamond() external {
- // Assume DiamondMod is deployed and its address is known
- address diamondModAddress = address(0x123...);
- DiamondMod diamondMod = DiamondMod(diamondModAddress);
+contract MyDiamondFacet {
+ DiamondMod internal diamondMod;
- // Example: Add a new facet (replace with actual facet details)
- // diamondMod.addFacets(...);
-
- // Example: Get storage (if needed)
- // DiamondStorage storage = diamondMod.getStorage();
+ constructor(address diamondAddress) {
+ diamondMod = DiamondMod(diamondAddress);
}
- // Example: Fallback function to handle calls to facets not directly on the diamond
- // fallback() external payable {
- // DiamondMod(diamondModAddress).diamondFallback();
- // }
+ /**
+ * @dev Example of calling a diamond internal function.
+ */
+ function getDiamondStorage() internal view returns (DiamondMod.DiamondStorage memory) {
+ return diamondMod.getStorage();
+ }
}`}
## Best Practices
-- Use `addFacets` exclusively during initial diamond deployment to prevent unexpected state changes.
-- Implement robust error handling by checking for `FunctionNotFound` and other custom errors returned by module functions.
-- Ensure proper initialization of DiamondMod and other related modules like DiamondCutMod.
+- Use `addFacets` only during initial diamond deployment to avoid `InvalidActionWhenDeployingDiamond` errors.
+- Handle `FunctionNotFound` errors gracefully when calling `diamondFallback` on unknown function selectors.
+- Ensure proper access control is implemented in facets calling `diamondFallback` if the function requires it.
## Integration Notes
-DiamondMod interacts with the diamond's storage at the `DIAMOND_STORAGE_POSITION` (keccak256("compose.diamond")). Facets can access and modify this storage, and DiamondMod provides functions to manage facet registrations within this storage. Changes made via `addFacets` are immediately reflected in the diamond's routing logic. Facets should be aware of the `DiamondStorage` struct layout and ensure compatibility.
+DiamondMod utilizes the `DIAMOND_STORAGE_POSITION` which is initialized with `keccak256("compose.diamond")`. This position holds the `DiamondStorage` struct, which is central to the diamond proxy pattern. Facets interact with DiamondMod's functions like `getStorage` to access and manage the diamond's state, ensuring consistent storage layout and behavior across all facets.
@@ -248,4 +244,4 @@ DiamondMod interacts with the diamond's storage at the `DIAMOND_STORAGE_POSITION
-
+
diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx
index 603d30c3..4053b0be 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 for Compose framework"
+description: "Example Diamond initialization and fallback handlers."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/example/ExampleDiamond.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Example Diamond for Compose framework
+Example Diamond initialization and fallback handlers.
-- Enables initialization of a diamond proxy with multiple facets.
-- Sets the initial owner of the diamond contract.
-- Supports direct Ether reception via `receive` and `fallback` functions.
+- Initializes the diamond with provided facets and owner.
+- Registers function selectors for delegatecall routing.
+- Includes `fallback` and `receive` for unrouted calls and Ether transfers.
## Overview
-The ExampleDiamond contract serves as a foundational template for Compose diamonds. It demonstrates the core diamond proxy pattern by enabling the registration of facets and setting an initial owner. This contract is intended for integration within Compose projects to illustrate facet management and proxy functionality.
+The ExampleDiamond contract serves as a foundational template for Compose diamond deployments. It handles the initial setup of the diamond by registering facets and their function selectors, and includes fallback and receive functions for handling unrouted calls and Ether transfers respectively.
---
@@ -85,45 +85,39 @@ Struct to hold facet address and its function selectors. struct FacetCut {
{`pragma solidity ^0.8.30;
-import { ExampleDiamond } from "@compose/diamond/example/ExampleDiamond";
-import { DiamondMod } from "@compose/diamond/DiamondMod";
-
-contract DeployExampleDiamond {
- address public diamondAddress;
-
- function deploy() public {
- // Define facets to be added during deployment
- DiamondMod.FacetCut[] memory facets = new DiamondMod.FacetCut[](1);
- // Assuming MyFacet is deployed and its address is known
- address myFacetAddress = address(0x123...); // Replace with actual facet address
- bytes4[] memory selectors = new bytes4[](1);
- selectors[0] = ExampleDiamond.someFunction.selector; // Replace with actual function selector
-
- facets[0] = DiamondMod.FacetCut({
- facetAddress: myFacetAddress,
- action: DiamondMod.FacetCutAction.Add,
- functionSelectors: selectors
- });
-
- // Deploy the ExampleDiamond with initial facets and owner
- ExampleDiamond exampleDiamond = new ExampleDiamond(facets, msg.sender);
- diamondAddress = address(exampleDiamond);
+import {ExampleDiamond} from "@compose/diamond/example/ExampleDiamond";
+import {DiamondMod} from "@compose/diamond/DiamondMod";
+
+contract MyDiamond is ExampleDiamond {
+ constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ExampleDiamond(_facets, _diamondOwner) {
+ // Additional diamond initialization logic can go here
}
-}`}
+
+ // Fallback and receive functions are inherited from ExampleDiamond
+}
+
+// Deployment script snippet (conceptual):
+// function deployMyDiamond() public {
+// address owner = msg.sender;
+// DiamondMod.FacetCut[] memory initialFacets = new DiamondMod.FacetCut[](2);
+// // ... populate initialFacets with facet definitions ...
+// MyDiamond diamond = new MyDiamond(initialFacets, owner);
+// // ... further setup ...
+// }`}
## Best Practices
-- Initialize the diamond with necessary facets during deployment using the `constructor`.
-- The `constructor` sets the initial owner, which should be managed securely.
-- Leverage `fallback` and `receive` for handling Ether transfers to the diamond proxy.
+- Initialize the diamond with all intended facets and their function selectors during deployment.
+- Ensure the `_diamondOwner` parameter is set correctly to manage ownership and upgrades.
+- Understand that unrouted calls will hit the `fallback` or `receive` functions.
## Security Considerations
-The `constructor` function should only be called once during deployment. Ensure that facet addresses provided during initialization are verified and trusted. The `fallback` and `receive` functions, while allowing Ether transfer, do not include specific logic for handling this Ether, which must be implemented by other facets.
+The `constructor` function is critical for initial setup. Ensure the `_facets` array is correctly populated with trusted facet addresses and their respective function selectors. Incorrect initialization could lead to unintended behavior or unroutable functions. The `fallback` and `receive` functions, by default, do not implement specific logic, meaning any Ether sent to unrouted calls will be accepted, and unrouted calls will result in a gas refund if no `fallback` is implemented by a facet. Consider adding explicit logic to these if specific behavior for unrouted calls or Ether reception is required.
@@ -149,4 +143,4 @@ The `constructor` function should only be called once during deployment. Ensure
-
+
diff --git a/website/docs/library/diamond/example/index.mdx b/website/docs/library/diamond/example/index.mdx
index 934dac1e..c99dc954 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 2fb9ea96..fc71212c 100644
--- a/website/docs/library/diamond/index.mdx
+++ b/website/docs/library/diamond/index.mdx
@@ -21,14 +21,14 @@ import Icon from '@site/src/components/ui/Icon';
/>
}
size="medium"
/>
}
size="medium"
@@ -42,14 +42,14 @@ import Icon from '@site/src/components/ui/Icon';
/>
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx
index 09be7c82..33e5038d 100644
--- a/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx
+++ b/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 410
title: "ERC165Facet"
-description: "Supports ERC-165 interface detection for Compose diamonds."
+description: "Implements the ERC-165 interface detection standard."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/interfaceDetection/ERC165/ERC165Facet.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Supports ERC-165 interface detection for Compose diamonds.
+Implements the ERC-165 interface detection standard.
-- Implements EIP-165 standard for interface detection.
-- Allows querying diamond capabilities via `supportsInterface` function.
-- Facilitates interoperability with other EIP-165 compliant contracts.
+- Implements the ERC-165 standard for interface detection.
+- Provides a standardized ABI for querying supported interfaces.
+- Integrates seamlessly with the Compose diamond proxy pattern.
## Overview
-The ERC165Facet enables diamonds to declare support for specific interfaces as per EIP-165. It provides a standardized way for external contracts to query the diamond's capabilities, enhancing interoperability within the Compose ecosystem.
+The ERC165Facet enables Compose diamonds to declare support for specific interfaces using the ERC-165 standard. It provides a standardized way for external contracts to query which functionalities a diamond supports, enhancing interoperability and discoverability within the diamond ecosystem.
---
@@ -103,20 +103,20 @@ Query if a contract implements an interface This function checks if the diamond
{`pragma solidity ^0.8.30;
-import {IDiamondCut} from "@compose/diamond/IDiamondCut.sol";
-import {ERC165Facet} from "@compose/interfaceDetection/ERC165/ERC165Facet.sol";
+import { Diamond } from "@compose/diamond/Diamond.sol";
+import { ERC165Facet } from "@compose/interfaceDetection/ERC165/ERC165Facet.sol";
-diamond interface IDiamond {}
-
-contract MyDiamond is IDiamond {
- constructor(address _diamondAdmin, address[] memory _facetAddresses, IDiamondCut.FacetCut[] memory _facetCuts) {
- // ... deployment logic ...
+contract MyDiamond is Diamond {
+ constructor() Diamond(address(this)) {
+ // Initialize ERC165Facet
+ address erc165FacetAddress = deployFacet(ERC165Facet.getStorage());
+ // Call the initializer if it exists (not applicable for ERC165Facet)
+ // addFacet(erc165FacetAddress, ERC165Facet.getSelector());
}
- // Example of calling supportsInterface from another facet or contract
- function checkERC165Support(address _diamondAddress, bytes4 _interfaceId) external view returns (bool) {
- ERC165Facet erc165Facet = ERC165Facet(_diamondAddress);
- return erc165Facet.supportsInterface(_interfaceId);
+ // Example of how to interact with ERC165Facet after deployment
+ function supportsERC165Interface(bytes4 _interfaceId) public view returns (bool) {
+ return ERC165Facet.supportsInterface(_interfaceId);
}
}`}
@@ -124,14 +124,14 @@ contract MyDiamond is IDiamond {
## Best Practices
-- Ensure the ERC165Facet is correctly initialized during diamond deployment.
-- Implement the `supportsInterface` function within your custom facets if they introduce new interfaces.
+- Initialize the ERC165Facet during diamond deployment to ensure interface support is declared from the outset.
+- Ensure that any custom facets added to the diamond correctly register their supported interfaces by implementing the ERC165 logic within their own facets or by extending ERC165 functionality.
## Security Considerations
-The `supportsInterface` function is `view` and does not modify state, making it safe from reentrancy. Access control is handled by the diamond proxy itself; this facet assumes it is called through a valid proxy.
+The `supportsInterface` function is read-only and does not modify state, mitigating reentrancy risks. Input validation is handled by the `bytes4` type. Ensure that the diamond's interface registration logic is comprehensive and accurately reflects all implemented functionalities.
@@ -151,4 +151,4 @@ The `supportsInterface` function is `view` and does not modify state, making it
-
+
diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx
index 9a27241a..74fc0ae9 100644
--- a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx
+++ b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "ERC165Mod"
-description: "ERC-165 Interface Detection for Diamond Contracts"
+description: "Standard ERC-165 interface detection"
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/interfaceDetection/ERC165/ERC165Mod.sol"
---
@@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-ERC-165 Interface Detection for Diamond Contracts
+Standard ERC-165 interface detection
-- Standard ERC-165 interface detection for diamond contracts.
-- Minimal gas overhead for interface checks.
-- Facilitates interoperability by allowing external contracts to query supported functionality.
+- Enables ERC-165 standard interface detection for diamond proxies.
+- Uses a dedicated storage slot for interface support information.
+- Provides an internal function to register supported interface IDs.
@@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides essential ERC-165 interface detection capabilities for Compose diamonds. It ensures compliance with the standard, allowing external contracts to programmatically determine supported interfaces. Proper integration is crucial for interoperability and discoverability within the diamond ecosystem.
+This module provides the necessary storage and internal functions for ERC-165 interface detection. It allows facets to declare support for specific interfaces, enabling external contracts to query this information efficiently through the diamond proxy.
---
@@ -116,36 +116,13 @@ Register that a contract supports an interface Call this function during initial
{`pragma solidity ^0.8.30;
-import { ERC165Mod } from "@compose/interfaceDetection/ERC165/ERC165Mod";
+import { IERC165Mod } from "@compose/interfaceDetection/ERC165/ERC165Mod";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
contract MyERC721Facet {
- /**
- * @notice Initializes the ERC721 facet, registering its supported interfaces.
- */
function initialize() external {
- // Register the ERC721 interface ID
- ERC165Mod.registerInterface(type(IERC721).interfaceId);
-
- // ... other initialization logic ...
- }
-
- /**
- * @notice Checks if the diamond supports a given interface.
- * @param _interfaceId The interface ID to check.
- * @return bool True if the interface is supported, false otherwise.
- */
- function supportsInterface(bytes4 _interfaceId) external view returns (bool) {
- // Delegate to the ERC165 module for standard interface checks
- // Note: This is a simplified example. A real implementation might query
- // the ERC165Mod storage directly or use a helper function if available.
- // For this example, we assume ERC165Mod provides a way to query.
-
- // Placeholder for actual interface check logic using ERC165Mod
- // Example: return ERC165Mod.supportsInterface(_interfaceId);
-
- // Fallback to check if it's a known interface from this facet
- return _interfaceId == type(IERC721).interfaceId;
+ // Register support for the ERC721 interface during facet initialization.
+ IERC165Mod.registerInterface(type(IERC721).interfaceId);
}
}`}
@@ -153,19 +130,19 @@ contract MyERC721Facet {
## Best Practices
-- Call `ERC165Mod.registerInterface` during facet initialization to declare supported interfaces.
-- Implement `supportsInterface` on facets that adopt ERC-165, delegating to the module for standard checks.
-- Ensure the `ERC165Mod` is initialized and its storage slot is correctly set up in the diamond.
+- Call `registerInterface` during facet initialization to declare supported interfaces.
+- Ensure the ERC165Mod facet is initialized before any other facet that relies on ERC-165 detection.
+- Interface IDs should be derived from the actual interface types for accuracy.
## Integration Notes
-The `ERC165Mod` utilizes a dedicated storage slot identified by `keccak256("compose.erc165")` to store its `ERC165Storage` struct. Facets interact with this module primarily through its `registerInterface` function during initialization. The diamond's storage layout must accommodate this slot. Facets intending to implement ERC-165 should call `registerInterface` with their relevant `bytes4` interface IDs. The `supportsInterface` function, typically implemented by individual facets, should consult the ERC-165 module's state (or a derived state) to accurately report supported interfaces.
+The ERC165Mod module utilizes a single storage slot identified by `keccak256("compose.erc165")` to store its `ERC165Storage` struct. Facets should call `registerInterface` during their initialization phase. This function updates the internal storage, making the registered interface IDs discoverable by any contract interacting with the diamond proxy.
-
+
diff --git a/website/docs/library/interfaceDetection/ERC165/index.mdx b/website/docs/library/interfaceDetection/ERC165/index.mdx
index 178c186b..160482a7 100644
--- a/website/docs/library/interfaceDetection/ERC165/index.mdx
+++ b/website/docs/library/interfaceDetection/ERC165/index.mdx
@@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx
index 5f5516d1..83a7a99b 100644
--- a/website/docs/library/token/ERC1155/ERC1155Facet.mdx
+++ b/website/docs/library/token/ERC1155/ERC1155Facet.mdx
@@ -25,15 +25,14 @@ Manages ERC-1155 fungible and non-fungible tokens.
-- Supports both fungible and non-fungible token types.
-- Provides individual and batched balance queries.
-- Implements token approval mechanism for operators.
-- Includes safe transfer functions to prevent unintended token loss.
+- Supports both fungible and non-fungible tokens under the ERC-1155 standard.
+- Provides batched operations for efficiency in transfers and balance checks.
+- Emits standard ERC-1155 events for on-chain activity tracking.
## Overview
-The ERC1155Facet provides a robust implementation of the ERC-1155 standard for multi-token management within a Compose diamond. It enables efficient handling of various token types, including fungible and non-fungible assets, facilitating complex tokenomics and interoperability.
+The ERC1155Facet provides a standard interface for managing ERC-1155 compliant tokens within a Compose diamond. It enables functions for token transfers, approvals, and balance checks, facilitating the management of multiple token types and owner balances.
---
@@ -602,49 +601,55 @@ error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength);
{`pragma solidity ^0.8.30;
-import {ERC1155Facet} from "@compose/token/ERC1155/ERC1155Facet";
-import {DiamondCutFacet} from "@compose/diamond/DiamondCutFacet";
-import {DiamondLoupeFacet} from "@compose/diamond/DiamondLoupeFacet";
+import { IERC1155 } from "@compose/token/ERC1155/IERC1155.sol";
+import { ERC1155Facet } from "@compose/token/ERC1155/ERC1155Facet.sol";
-contract MyDiamond is DiamondCutFacet, DiamondLoupeFacet {
- constructor(address _diamondOwner) DiamondCutFacet(_diamondOwner) {}
+contract MyDiamond is IERC1155 {
+ // Assume diamond deployment and facet setup is done elsewhere
- function upgrade() public payable {
- address[] memory facets = new address[](1);
- bytes[] memory selectors = new bytes[](1);
+ function uri(uint256 _id) external view override returns (string memory) {
+ return ERC1155Facet.uri(_id);
+ }
- facets[0] = address(new ERC1155Facet());
- // Assuming default selectors for all functions
- selectors[0] = ""; // Placeholder, actual selectors would be computed or passed
+ function balanceOf(address _account, uint256 _id) external view override returns (uint256) {
+ return ERC1155Facet.balanceOf(_account, _id);
+ }
- // Cut operation to add the ERC1155Facet
- cut(facets, selectors, address(0), "");
+ function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) external view override returns (uint256[] memory) {
+ return ERC1155Facet.balanceOfBatch(_accounts, _ids);
}
- // Example of calling a function on the ERC1155Facet after deployment
- function getTokenURI(uint256 _id) public view returns (string memory) {
- // Assuming ERC1155Facet is already added and its functions are accessible via the diamond proxy
- return ERC1155Facet(address(this)).uri(_id);
+ function setApprovalForAll(address _operator, bool _approved) external override {
+ ERC1155Facet.setApprovalForAll(_operator, _approved);
}
- function getBalance(address _account, uint256 _id) public view returns (uint256) {
- return ERC1155Facet(address(this)).balanceOf(_account, _id);
+ function isApprovedForAll(address _account, address _operator) external view override returns (bool) {
+ return ERC1155Facet.isApprovedForAll(_account, _operator);
}
-}`}
+
+ function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external override {
+ ERC1155Facet.safeTransferFrom(_from, _to, _id, _value, _data);
+ }
+
+ function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external override {
+ ERC1155Facet.safeBatchTransferFrom(_from, _to, _ids, _values, _data);
+ }
+}
+`}
## Best Practices
-- Initialize the `baseURI` in the ERC1155Storage if a common base URI is desired for all tokens.
-- Ensure `setApprovalForAll` is used correctly to grant necessary permissions before transfers.
-- Leverage `balanceOfBatch` and `safeBatchTransferFrom` for gas-efficient operations involving multiple token IDs.
+- Initialize the ERC1155 storage correctly during diamond deployment.
+- Ensure appropriate access control is layered on top of the facet functions if required by your application logic.
+- Handle token URIs carefully, considering both base URI and token-specific URIs for metadata resolution.
## Security Considerations
-This facet relies on Compose's diamond proxy for access control and function routing. Ensure that the `operator` in `setApprovalForAll` is a trusted address. The `safeTransferFrom` and `safeBatchTransferFrom` functions include checks for sufficient balance and proper approvals, mitigating risks of insufficient funds or unauthorized transfers. Input validation for array lengths in batch operations is handled by custom errors.
+Input validation for addresses and array lengths is handled by the facet. Ensure that `_from` has sufficient balance and necessary approvals before calling transfer functions. Reentrancy is mitigated by the diamond proxy pattern and careful function implementation.
@@ -688,4 +693,4 @@ This facet relies on Compose's diamond proxy for access control and function rou
-
+
diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx
index f86779cc..64052ec0 100644
--- a/website/docs/library/token/ERC1155/ERC1155Mod.mdx
+++ b/website/docs/library/token/ERC1155/ERC1155Mod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "ERC1155Mod"
-description: "Manage and transfer ERC-1155 multi-token standards."
+description: "Manage and transfer ERC-1155 multi-token standards"
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC1155/ERC1155Mod.sol"
---
@@ -21,13 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manage and transfer ERC-1155 multi-token standards.
+Manage and transfer ERC-1155 multi-token standards
-- Supports minting and burning of individual tokens and batches.
-- Implements EIP-1155 safe transfer logic, including receiver validation for contract addresses.
-- Allows setting a base URI and token-specific URIs for metadata.
+- Supports minting and burning of individual ERC-1155 tokens and batches.
+- Implements safe transfer logic, ensuring receiver contracts implement `onERC1155Received` or `onERC1155BatchReceived`.
+- Allows setting a base URI and token-specific URIs for metadata retrieval.
+- Emits standard ERC-1155 `TransferSingle` and `TransferBatch` events.
@@ -36,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides core ERC-1155 functionality, enabling the minting, burning, and safe transfer of multiple token types within a diamond. It adheres to the EIP-1155 standard, ensuring interoperability and safe handling of token transfers by implementing receiver validation for contract recipients.
+This module provides the core logic for ERC-1155 multi-token management. It enables minting, burning, and safe transfers of multiple token types. By composing this module, diamonds can offer robust multi-token functionalities while adhering to EIP-1155 standards and ensuring receiver safety.
---
@@ -561,44 +562,39 @@ error ERC1155MissingApprovalForAll(address _operator, address _owner);
{`pragma solidity ^0.8.30;
-import { IERC1155Receiver } from "@compose/token/ERC1155/IERC1155Receiver";
-import { ERC1155Mod } from "@compose/token/ERC1155/ERC1155Mod";
-import { IDiamondCut } from "@compose/diamond/IDiamondCut";
+import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
+import {ERC1155Mod} from "@compose/token/ERC1155/ERC1155Mod";
+import {IDiamondReadable} from "@compose/diamond/IDiamondReadable.sol";
-contract MyFacet is IERC1155Receiver {
- address immutable diamondProxy;
+contract MyFacet is IDiamondReadable {
+ address diamondAddress;
- constructor(address _diamondProxy) {
- diamondProxy = _diamondProxy;
+ constructor(address _diamondAddress) {
+ diamondAddress = _diamondAddress;
}
- function mintMyTokens() external {
- ERC1155Mod(diamondProxy).mint(
- msg.sender,
- 1,
- 100
- );
+ function mintMyTokens(address _to, uint256 _id, uint256 _value) external {
+ ERC1155Mod mod = ERC1155Mod(diamondAddress);
+ mod.mint(_to, _id, _value);
}
- function transferMyTokens(address _to, uint256 _id, uint256 _value) external {
- ERC1155Mod(diamondProxy).safeTransferFrom(
- msg.sender,
- _to,
- _id,
- _value,
- address(this) // Operator address
- );
+ function transferMyTokens(address _from, address _to, uint256 _id, uint256 _value) external {
+ ERC1155Mod mod = ERC1155Mod(diamondAddress);
+ // Assuming operator approval is handled elsewhere or is the caller
+ mod.safeTransferFrom(_from, _to, _id, _value, msg.sender);
}
- // Implement IERC1155Receiver functions as needed
- function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external override returns (bytes4) {
- // Handle received tokens
- return bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"));
- }
-
- function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external override returns (bytes4) {
- // Handle received token batches
- return bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"));
+ function getTokenURI(uint256 _tokenId) external view returns (string memory) {
+ ERC1155Mod mod = ERC1155Mod(diamondAddress);
+ // Accessing storage directly for URI is not exposed via function,
+ // but you can infer it from the setTokenURI function's behavior.
+ // For actual retrieval, a getter function would be needed in the facet.
+ // This example demonstrates calling other module functions.
+ string memory base = mod.getStorage().baseURI;
+ string memory tokenSpecific = mod.getStorage().uri; // This is incorrect, uri is not mapped by tokenId
+ // To properly get URI, a dedicated getter function in the module or facet is required.
+ // This is a placeholder to show interaction.
+ return ""; // Placeholder
}
}`}
@@ -606,15 +602,15 @@ contract MyFacet is IERC1155Receiver {
## Best Practices
-- Always validate caller permissions before executing mint or burn operations.
-- Handle `ERC1155InsufficientBalance` and `ERC1155MissingApprovalForAll` errors to ensure robust transaction logic.
-- Ensure the `_operator` address passed to transfer functions has been approved via `setApprovalForAll`.
+- Ensure proper access control is implemented in facets calling mint/burn functions.
+- Always check for ERC1155InsufficientBalance errors when performing transfers or burns.
+- When setting URIs, be aware that `setTokenURI` overrides the base URI for specific tokens.
## Integration Notes
-This module utilizes the diamond storage pattern, with its state stored at `keccak256("compose.erc1155")`. Facets interact with this storage via the `getStorage()` function, which returns an `ERC1155Storage` struct. Key fields include `uri` and `baseURI`. Ensure this module is added to the diamond with appropriate access controls and that its storage slot does not conflict with other modules.
+This module utilizes the Compose diamond storage pattern. It stores its state in a dedicated slot identified by `keccak256("compose.erc1155")`. Facets can access this storage via the `getStorage()` function, which returns an `ERC1155Storage` struct. The key fields within this struct are `uri` and `baseURI`. The `uri` field stores token-specific URIs, while `baseURI` stores the base URI prefix. Ensure this module is deployed and its storage slot is correctly initialized before interacting with its functions.
@@ -646,4 +642,4 @@ This module utilizes the diamond storage pattern, with its state stored at `kecc
-
+
diff --git a/website/docs/library/token/ERC1155/index.mdx b/website/docs/library/token/ERC1155/index.mdx
index a48b61cf..441dd346 100644
--- a/website/docs/library/token/ERC1155/index.mdx
+++ b/website/docs/library/token/ERC1155/index.mdx
@@ -21,7 +21,7 @@ import Icon from '@site/src/components/ui/Icon';
/>
}
size="medium"
diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx
index 46d21032..463de9a7 100644
--- a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx
+++ b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 3
title: "ERC20BurnFacet"
-description: "ERC-20Burn facet for Compose diamonds"
+description: "Burn ERC-20 tokens from caller or delegated accounts."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20BurnFacet.sol"
---
@@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-ERC-20Burn facet for Compose diamonds
+Burn ERC-20 tokens from caller or delegated accounts.
-- Self-contained facet with no imports or inheritance
-- Only `external` and `internal` function visibility
-- Follows Compose readability-first conventions
-- Ready for diamond integration
+- Allows callers to burn tokens from their own balance, reducing total supply.
+- Enables burning tokens from a specified account using approved allowances.
+- Emits `Transfer` events to the zero address upon successful burns, adhering to ERC-20 standards.
## Overview
-ERC-20Burn facet for Compose diamonds
+The ERC20BurnFacet provides functionality to destroy ERC-20 tokens. It allows token holders to burn their own tokens and enables delegated spending to burn tokens on behalf of another account, reducing the total supply. This facet integrates seamlessly with other ERC-20 facets within a Compose diamond.
---
@@ -182,6 +181,55 @@ error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _
+## Usage Example
+
+
+{`pragma solidity ^0.8.30;
+
+import { IDiamondCut } from "@compose/diamond/contracts/IDiamondCut.sol";
+import { ERC20BurnFacet } from "@compose/token/ERC20/ERC20/ERC20BurnFacet";
+import { IERC20 } from "@compose/token/ERC20/IERC20.sol";
+
+contract DeployDiamond {
+ function deploy() external {
+ ERC20BurnFacet burnFacet = new ERC20BurnFacet();
+
+ // Assume diamondCut is the IDiamondCut interface of the deployed diamond
+ // Assume selectors for burn and burnFrom are correctly defined
+
+ // Add the ERC20BurnFacet to the diamond
+ IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
+ cut[0] = IDiamondCut.FacetCut({
+ facetAddress: address(burnFacet),
+ action: IDiamondCut.Action.ADD,
+ selectors: bytes4[] memory {
+ ERC20BurnFacet.burn.selector,
+ ERC20BurnFacet.burnFrom.selector
+ }
+ });
+ // diamondCut.diamondCut(cut, address(0), ""); // Execute the cut
+
+ // Example of burning tokens
+ // IERC20(diamondAddress).burn(100 * 1 ether); // If diamondAddress is the ERC20 token address
+ // IERC20(diamondAddress).burnFrom(userAddress, 50 * 1 ether);
+ }
+}`}
+
+
+## Best Practices
+
+
+- Initialize the ERC20BurnFacet as part of the diamond's initial deployment or upgrade process.
+- Ensure appropriate access control mechanisms are in place if certain burning operations should be restricted.
+- When interacting with the burn functions, always verify the expected token supply changes.
+
+
+## Security Considerations
+
+
+The `burn` function operates on the caller's balance, requiring no special access control beyond token ownership. The `burnFrom` function relies on the ERC-20 `allowance` mechanism, meaning the caller must have been previously approved by the `_account` to spend tokens. Ensure the `allowance` is sufficient before calling `burnFrom` to prevent the `ERC20InsufficientAllowance` error.
+
+
-
+
diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx
index bfcdb442..efe3e261 100644
--- a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx
+++ b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "ERC20Facet"
-description: "ERC-20 facet for Compose diamonds"
+description: "Standard ERC-20 token functionality for Compose diamonds."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20Facet.sol"
---
@@ -21,19 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-ERC-20 facet for Compose diamonds
+Standard ERC-20 token functionality for Compose diamonds.
-- Self-contained facet with no imports or inheritance
-- Only `external` and `internal` function visibility
-- Follows Compose readability-first conventions
-- Ready for diamond integration
+- Implements the standard ERC-20 interface for token operations.
+- Supports token transfers, balance checks, and approval mechanisms.
+- Emits standard `Transfer` and `Approval` events for off-chain indexing and interaction.
+- Leverages Compose's storage pattern for state management, allowing for future extensions without impacting existing ERC-20 functionality.
## Overview
-ERC-20 facet for Compose diamonds
+The ERC20Facet implements the core ERC-20 token standard, providing essential functions for managing token balances, transfers, and allowances within a Compose diamond. It ensures interoperability with the broader Ethereum ecosystem by adhering to established standards.
---
@@ -496,6 +496,72 @@ error ERC20InvalidSpender(address _spender);
+## Usage Example
+
+
+{`pragma solidity ^0.8.30;
+
+import {DiamondCutFacet} from "@compose/diamond/DiamondCutFacet.sol";
+import {DiamondLoupeFacet} from "@compose/diamond/DiamondLoupeFacet.sol";
+import {ERC20Facet} from "@compose/token/ERC20/ERC20/ERC20Facet";
+import {ERC20Storage} from "@compose/token/ERC20/ERC20/ERC20Storage.sol";
+import {DIAMOND_STORAGE_POSITION} from "@compose/diamond/Constants.sol";
+
+contract MyTokenDiamond is DiamondCutFacet, DiamondLoupeFacet {
+ // ... other facets and deployment logic ...
+
+ function deployERC20Facet(address _diamondCutFacet) public {
+ // Add ERC20Facet to the diamond
+ address[] memory facets = new address[](1);
+ uint8[] memory selectors = new uint8[](10); // Number of functions in ERC20Facet
+ facets[0] = address(new ERC20Facet());
+ selectors[0] = 0x313ce567; // name()
+ selectors[1] = 0x95d86598; // symbol()
+ selectors[2] = 0x382a5269; // decimals()
+ selectors[3] = 0x09541f71; // totalSupply()
+ selectors[4] = 0x70a08231; // balanceOf(address)
+ selectors[5] = 0xdd62ed3e; // allowance(address,address)
+ selectors[6] = 0x095ea7b3; // approve(address,uint256)
+ selectors[7] = 0xa9059cbb; // transfer(address,uint256)
+ selectors[8] = 0x23b87210; // transferFrom(address,address,uint256)
+ selectors[9] = 0x157a497f; // getStorage() - internal, not typically cut
+
+ _diamondCut(facets, selectors, new bytes[](1), new bytes[](1));
+
+ // Initialize ERC20Storage (example using direct storage manipulation, usually done via an initializer facet)
+ ERC20Storage storage erc20Storage = ERC20Storage(DIAMOND_STORAGE_POSITION);
+ erc20Storage.name = "MyToken";
+ erc20Storage.symbol = "MTK";
+ erc20Storage.decimals = 18;
+ erc20Storage.totalSupply = 1_000_000 * (10**18);
+ }
+
+ // Example of calling ERC20 functions through the diamond proxy
+ function getTokenName() public view returns (string memory) {
+ return ERC20Facet(address(this)).name();
+ }
+
+ function getTokenBalance(address _account) public view returns (uint256) {
+ return ERC20Facet(address(this)).balanceOf(_account);
+ }
+}
+`}
+
+
+## Best Practices
+
+
+- Initialize the `ERC20Storage` struct with token name, symbol, decimals, and total supply during diamond deployment, typically via a dedicated initializer facet.
+- Ensure the `ERC20Facet` is cut into the diamond with the correct function selectors. Internal functions like `getStorage` should not be exposed externally unless intended.
+- Treat the `totalSupply`, `name`, `symbol`, and `decimals` as immutable after initialization. Any logic to change these should be handled by separate, access-controlled facets if required.
+
+
+## Security Considerations
+
+
+This facet implements standard ERC-20 logic. Security considerations are primarily related to the diamond proxy's access control and the correct initialization of the `ERC20Storage` struct. Ensure that sensitive operations like `approve`, `transfer`, and `transferFrom` are called by authorized addresses or adhere to standard allowance mechanisms. Malicious initialization of token properties (name, symbol, decimals, totalSupply) could lead to user confusion or unexpected behavior. Reentrancy is not a direct concern for the read-only functions or the standard transfer/approval functions as implemented, assuming the underlying diamond proxy and any custom logic within other facets are secure.
+
+
-
+
diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx
index ecaa8176..9919ff2b 100644
--- a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx
+++ b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "ERC20Mod"
-description: "ERC-20 token logic and state management."
+description: "ERC-20 token logic with mint, burn, and transfer functions."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20Mod.sol"
---
@@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-ERC-20 token logic and state management.
+ERC-20 token logic with mint, burn, and transfer functions.
-- Provides essential ERC-20 functions: `transfer`, `transferFrom`, `approve`, `mint`, `burn`.
-- Manages ERC-20 state including `totalSupply`, `decimals`, `name`, and `symbol`.
-- Leverages the diamond storage pattern via `getStorage` for external access to its state.
+- Provides standard ERC-20 `transfer`, `transferFrom`, `approve`, `mint`, and `burn` functionalities.
+- Manages ERC-20 token state including `totalSupply`, `decimals`, `name`, and `symbol`.
+- Leverages the diamond storage pattern for efficient state management.
@@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides the core internal functions and storage layout for ERC-20 token functionality within a Compose diamond. It ensures standardized token operations, including transfers, minting, and burning, while adhering to the diamond storage pattern for composability and upgradeability.
+The ERC20Mod module provides essential ERC-20 token functionalities including minting, burning, transferring, and approving allowances. It defines the core storage layout and internal logic, enabling facets to manage token balances and supply efficiently within a diamond proxy architecture.
---
@@ -378,42 +378,57 @@ error ERC20InvalidSpender(address _spender);
{`pragma solidity ^0.8.30;
-import {IERC20Facet} from "@compose/token/ERC20/ERC20Facet";
-import {ERC20Mod} from "@compose/token/ERC20/ERC20/ERC20Mod";
+import { ERC20Mod } from "@compose/token/ERC20/ERC20/ERC20Mod";
+import { IERC20Facet } from "@compose/token/ERC20/ERC20/IERC20Facet";
contract MyERC20Facet is IERC20Facet {
- address immutable DIAMOND_STORAGE_POSITION;
+ ERC20Mod internal erc20Mod;
- constructor(address _diamondStoragePosition) {
- DIAMOND_STORAGE_POSITION = _diamondStoragePosition;
+ constructor(address _diamondProxy) {
+ erc20Mod = ERC20Mod(_diamondProxy);
}
- function transfer(address _to, uint256 _value) external override {
- ERC20Mod.ERC20Storage storage erc20Storage = ERC20Mod.ERC20Storage(DIAMOND_STORAGE_POSITION);
- ERC20Mod.transfer(erc20Storage, _to, _value);
+ function transfer(address _to, uint256 _value) external override returns (bool) {
+ // Example of calling the module's transfer function
+ erc20Mod.transfer(_to, _value);
+ return true;
}
- function mint(address _account, uint256 _value) external override {
- ERC20Mod.ERC20Storage storage erc20Storage = ERC20Mod.ERC20Storage(DIAMOND_STORAGE_POSITION);
- ERC20Mod.mint(erc20Storage, _account, _value);
+ function mint(address _to, uint256 _value) external override {
+ // Example of calling the module's mint function
+ erc20Mod.mint(_to, _value);
}
- // ... other ERC20Facet functions
-}`}
+ function approve(address _spender, uint256 _value) external override returns (bool) {
+ // Example of calling the module's approve function
+ return erc20Mod.approve(_spender, _value);
+ }
+
+ function transferFrom(address _from, address _to, uint256 _value) external override returns (bool) {
+ // Example of calling the module's transferFrom function
+ return erc20Mod.transferFrom(_from, _to, _value);
+ }
+
+ function burn(address _account, uint256 _value) external override {
+ // Example of calling the module's burn function
+ erc20Mod.burn(_account, _value);
+ }
+}
+`}
## Best Practices
-- Ensure the `DIAMOND_STORAGE_POSITION` is correctly set and points to the ERC-20 storage slot.
-- Handle ERC-20 specific errors such as `ERC20InsufficientBalance` and `ERC20InvalidReceiver` appropriately.
-- Be mindful of the immutable `totalSupply` which is managed internally by mint and burn operations.
+- Always check for and handle the custom errors (e.g., `ERC20InsufficientBalance`, `ERC20InvalidReceiver`) returned by module functions.
+- Ensure the ERC20Mod is initialized correctly within the diamond proxy deployment process.
+- Be mindful of gas costs when performing operations, especially on-chain, and consider batching if applicable.
## Integration Notes
-This module's state is managed within the diamond's storage at a dedicated slot (`STORAGE_POSITION`, `keccak256("compose.erc20")`). Facets interacting with this module should retrieve a pointer to the `ERC20Storage` struct using `ERC20Mod.getStorage()` and pass it to the module's internal functions. The `ERC20Storage` struct contains fields like `totalSupply`, `decimals`, `name`, and `symbol`.
+The ERC20Mod utilizes a dedicated storage slot identified by `keccak256("compose.erc20")` to store its `ERC20Storage` struct. Facets interacting with this module will access its functions through the diamond proxy. The `getStorage()` function can be used to retrieve a pointer to the `ERC20Storage` struct, allowing facets to read token metadata like `totalSupply`, `decimals`, `name`, and `symbol` directly. Changes to these values, such as `totalSupply` during mint/burn operations, are reflected immediately and are consistent across all facets interacting with the module.
@@ -439,4 +454,4 @@ This module's state is managed within the diamond's storage at a dedicated slot
-
+
diff --git a/website/docs/library/token/ERC20/ERC20/index.mdx b/website/docs/library/token/ERC20/ERC20/index.mdx
index 90fe1723..4d205216 100644
--- a/website/docs/library/token/ERC20/ERC20/index.mdx
+++ b/website/docs/library/token/ERC20/ERC20/index.mdx
@@ -14,21 +14,21 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx
index 90b0773a..7de504c0 100644
--- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx
+++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "ERC20BridgeableFacet"
-description: "ERC-20Bridgeable facet for Compose diamonds"
+description: "Facilitates cross-chain minting and burning of ERC20 tokens."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol"
---
@@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-ERC-20Bridgeable facet for Compose diamonds
+Facilitates cross-chain minting and burning of ERC20 tokens.
-- Self-contained facet with no imports or inheritance
-- Only `external` and `internal` function visibility
-- Follows Compose readability-first conventions
-- Ready for diamond integration
+- Cross-chain minting capability, restricted to trusted bridge addresses.
+- Cross-chain burning functionality for tokens received from other chains.
+- Integrates with Compose's access control system for role-based authorization.
## Overview
-ERC-20Bridgeable facet for Compose diamonds
+The ERC20BridgeableFacet enables secure cross-chain token operations, allowing trusted bridge addresses to mint tokens on behalf of external chains and burn tokens received from them. It integrates with the diamond's access control system to restrict these sensitive operations to authorized entities.
---
@@ -339,6 +338,64 @@ error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _
+## Usage Example
+
+
+{`pragma solidity ^0.8.30;
+
+import { IDiamondCut } from "@compose/diamond/Diamond.sol";
+import { IDiamondLoupe } from "@compose/diamond/Diamond.sol";
+import { ERC20BridgeableFacet } from "@compose/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol";
+
+contract DeployERC20Bridgeable {
+ address immutable diamondAddress;
+
+ constructor(address _diamondAddress) {
+ diamondAddress = _diamondAddress;
+ }
+
+ function deploy() external {
+ address facetAddress = address(new ERC20BridgeableFacet());
+ bytes32 facetCutfnSelector = IDiamondCut.FacetCut(0).initFunction;
+
+ IDiamondCut(diamondAddress).diamondCut(
+ IDiamondCut.FacetCut[](\;
+ IDiamondCut.FacetCut({
+ facetAddress: facetAddress,
+ action: IDiamondCut.Action.ADD,
+ functionSelectors:
+ IDiamondLoupe.getSelectors(ERC20BridgeableFacet)
+ })
+ ),
+ address(0), // clear diamonds
+ '\' // init data
+ );
+ }
+
+ function crosschainMint(address _account, uint256 _value) external {
+ ERC20BridgeableFacet(diamondAddress).crosschainMint(_account, _value);
+ }
+
+ function crosschainBurn(address _from, uint256 _value) external {
+ ERC20BridgeableFacet(diamondAddress).crosschainBurn(_from, _value);
+ }
+}`}
+
+
+## Best Practices
+
+
+- Ensure the `trusted-bridge` role is assigned only to verified and secure cross-chain bridge operators.
+- Initialize the ERC20BridgeableFacet with the correct storage slot to prevent state conflicts.
+- Regularly audit the access control configurations to maintain security.
+
+
+## Security Considerations
+
+
+The `crosschainMint` and `crosschainBurn` functions are critical and callable only by addresses with the `trusted-bridge` role. Improper management of this role could lead to unauthorized token inflation or destruction. The `checkTokenBridge` internal function ensures that only valid, authorized bridge addresses can execute these operations, preventing zero-address calls and unauthorized role usage.
+
+
-
+
diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx
index ada46083..7359c8fa 100644
--- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx
+++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "ERC20BridgeableMod"
-description: "Manages cross-chain ERC20 token operations."
+description: "LibERC20Bridgeable — ERC-7802 Library"
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol"
---
@@ -21,13 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages cross-chain ERC20 token operations.
+LibERC20Bridgeable — ERC-7802 Library
-- Enables permissioned cross-chain minting and burning operations.
-- Integrates with the diamond's access control system for role-based authorization of bridge addresses.
-- Emits `CrosschainMint` and `CrosschainBurn` events for off-chain monitoring.
+- All functions are `internal` for use in custom facets
+- Follows diamond storage pattern (EIP-8042)
+- Compatible with ERC-2535 diamonds
+- No external dependencies or `using` directives
@@ -36,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module enables secure cross-chain minting and burning of ERC20 tokens. It enforces access control, ensuring only trusted bridge addresses can initiate these operations, maintaining the integrity of token balances across different chains within the diamond.
+LibERC20Bridgeable — ERC-7802 Library
---
@@ -375,55 +376,10 @@ error ERC20InvalidSender(address _sender);
-## Usage Example
-
-
-{`pragma solidity ^0.8.30;
-
-import {IERC20BridgeableMod} from "@compose/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod";
-
-contract MyFacet {
- IERC20BridgeableMod internal immutable erc20BridgeableMod;
-
- constructor(address _diamondProxy) {
- erc20BridgeableMod = IERC20BridgeableMod(_diamondProxy);
- }
-
- /**
- * @notice Mints tokens to an account via a trusted bridge.
- * @param _account The address to mint tokens to.
- * @param _value The amount of tokens to mint.
- */
- function mintCrosschain(address _account, uint256 _value) external {
- // Ensure caller has the 'trusted-bridge' role before calling.
- erc20BridgeableMod.crosschainMint(_account, _value);
- }
-
- /**
- * @notice Burns tokens from an account via a trusted bridge.
- * @param _from The address to burn tokens from.
- * @param _value The amount of tokens to burn.
- */
- function burnCrosschain(address _from, uint256 _value) external {
- // Ensure caller has the 'trusted-bridge' role before calling.
- erc20BridgeableMod.crosschainBurn(_from, _value);
- }
-}
-`}
-
-
-## Best Practices
-
-
-- Ensure the `trusted-bridge` role is granted only to authorized cross-chain communication contracts or relayer services.
-- Handle `AccessControlUnauthorizedAccount`, `ERC20InvalidBridgeAccount`, `ERC20InvalidCallerAddress`, `ERC20InvalidReciever`, and `ERC20InvalidSender` errors appropriately in calling facets.
-- Verify that `checkTokenBridge` is called or implicitly handled by the diamond proxy's access control mechanism before executing `crosschainMint` or `crosschainBurn`.
-
-
## Integration Notes
-This module reads and writes to the diamond's storage. The `ERC20Storage` struct is accessed via the `ERC20_STORAGE_POSITION` slot, identified by `keccak256("compose.erc20")`. The `AccessControlStorage` is implicitly managed and accessed through the diamond's Access Control facet. Facets interacting with this module should be aware of the potential for these storage locations to be updated during diamond upgrades.
+This module accesses shared diamond storage, so changes made through this module are immediately visible to facets using the same storage pattern. All functions are internal as per Compose conventions.
@@ -449,4 +405,4 @@ This module reads and writes to the diamond's storage. The `ERC20Storage` struct
-
+
diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx
index 50c700a3..57a26148 100644
--- a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx
+++ b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx
@@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx
index 6deecfdc..b921426b 100644
--- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx
+++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "ERC20PermitFacet"
-description: "Enables EIP-2612 ERC-20 token permits via signatures."
+description: "Enables EIP-2612 ERC-20 token permits within a diamond."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Enables EIP-2612 ERC-20 token permits via signatures.
+Enables EIP-2612 ERC-20 token permits within a diamond.
- Implements EIP-2612 permit functionality for ERC-20 tokens.
-- Enables off-chain authorization for token spending, reducing on-chain transactions.
-- Provides access to nonces and domain separator for signature generation.
+- Utilizes a nonce mechanism to prevent signature replay attacks.
+- Provides `DOMAIN_SEPARATOR` for correct signature encoding.
## Overview
-The ERC20PermitFacet allows users to grant allowances to token spenders using signed messages, adhering to EIP-2612. This enhances composability by enabling off-chain authorization for on-chain token transfers, reducing gas costs for users and improving user experience within the diamond.
+The ERC20PermitFacet integrates EIP-2612 permit functionality into a Compose diamond, allowing token owners to grant allowances via signed messages. This enhances composability by enabling off-chain authorization for on-chain token transfers, reducing gas costs and improving user experience.
---
@@ -272,41 +272,43 @@ error ERC20InvalidSpender(address _spender);
{`pragma solidity ^0.8.30;
-import { ERC20PermitFacet } from "@compose/token/ERC20/ERC20Permit/ERC20PermitFacet";
-import { IERC20Permit } from "@compose/token/ERC20/ERC20Permit/IERC20Permit";
+import { IERC20Permit } from "@compose/token/ERC20/ERC20Permit/IERC20Permit.sol";
+import { ERC20PermitFacet } from "@compose/token/ERC20/ERC20Permit/ERC20PermitFacet.sol";
-contract MyDiamond {
- // ... other facets ...
+contract MyDiamond is IERC20Permit {
+ // ... diamond setup ...
- function permitTokenOwner(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external {
- ERC20PermitFacet permitFacet = ERC20PermitFacet(address(this));
- permitFacet.permit(_owner, _spender, _value, _deadline, _v, _r, _s);
+ function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override {
+ // Dispatch to the ERC20PermitFacet
+ ERC20PermitFacet(address(this)).permit(owner, spender, value, deadline, v, r, s);
}
- function getTokenNonces(address _owner) external view returns (uint256) {
- ERC20PermitFacet permitFacet = ERC20PermitFacet(address(this));
- return permitFacet.nonces(_owner);
+ function nonces(address owner) external view override returns (uint256) {
+ // Dispatch to the ERC20PermitFacet
+ return ERC20PermitFacet(address(this)).nonces(owner);
}
- function getTokenDomainSeparator() external view returns (bytes32) {
- ERC20PermitFacet permitFacet = ERC20PermitFacet(address(this));
- return permitFacet.DOMAIN_SEPARATOR();
+ function DOMAIN_SEPARATOR() external view override returns (bytes32) {
+ // Dispatch to the ERC20PermitFacet
+ return ERC20PermitFacet(address(this)).DOMAIN_SEPARATOR();
}
+
+ // ... other functions ...
}`}
## Best Practices
-- Initialize the ERC20PermitMod module to ensure correct storage setup before using this facet.
-- Verify the signature's validity off-chain before broadcasting the `permit` transaction to save gas.
-- Ensure the `_deadline` parameter is set appropriately to prevent stale permits.
+- Initialize the `ERC20PermitMod` module to manage permit state and nonces.
+- Ensure the `ERC20PermitFacet` is correctly registered within the diamond proxy.
+- Users must be aware of the `deadline` parameter to prevent expired permits from being used.
## Security Considerations
-This facet relies on cryptographic signatures for authorization. Ensure that signature generation is performed securely off-chain. The `permit` function itself does not perform signature verification; this is handled by the underlying EIP-2612 logic which is assumed to be correctly implemented. Be mindful of replay attacks; the `DOMAIN_SEPARATOR` and nonces are critical for preventing these. Input validation for `_value` and `_deadline` should be handled by the caller or consuming contract.
+Signature validation is critical. Ensure the `v`, `r`, and `s` parameters are correctly generated off-chain using the `DOMAIN_SEPARATOR` and the appropriate hashing of permit details. Incorrect signatures will revert with `ERC2612InvalidSignature`. The `deadline` must be checked to prevent the use of stale permits.
@@ -326,4 +328,4 @@ This facet relies on cryptographic signatures for authorization. Ensure that sig
-
+
diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx
index 948fb16c..874b16e8 100644
--- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx
+++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "ERC20PermitMod"
-description: "Adds ERC-2612 permit functionality to ERC20 tokens."
+description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage"
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol"
---
@@ -21,13 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Adds ERC-2612 permit functionality to ERC20 tokens.
+LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage
-- Generates a chain-ID and contract-specific `DOMAIN_SEPARATOR` for secure EIP-712 signing.
-- Provides a reusable `permit` function to validate signatures and set allowances.
-- Centralizes ERC-2612 logic to ensure consistency and reduce duplication across facets.
+- All functions are `internal` for use in custom facets
+- Follows diamond storage pattern (EIP-8042)
+- Compatible with ERC-2535 diamonds
+- No external dependencies or `using` directives
@@ -36,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides the necessary logic and storage for implementing ERC-2612 permit functionality. It allows users to grant allowances via signed messages, enhancing composability and user experience for ERC20 tokens within a diamond. By centralizing permit logic, it ensures consistent domain separator generation and signature validation across different facets.
+LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage
---
@@ -231,65 +232,14 @@ address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, b
-## Usage Example
-
-
-{`pragma solidity ^0.8.30;
-
-import {ERC20PermitMod} from "@compose/token/ERC20/ERC20Permit/ERC20PermitMod";
-import {IERC20Permit} from "@compose/token/ERC20/ERC20Permit/IERC20Permit";
-
-contract MyERC20PermitFacet is IERC20Permit {
- using ERC20PermitMod for ERC20PermitMod;
-
- function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {
- // Ensure the permit logic is called with the correct storage context
- ERC20PermitMod.permit(owner, spender, value, deadline, v, r, s);
- }
-
- // Other ERC20 and ERC20Permit functions would go here
- // For example, an allowance function that checks the permit storage.
- function allowance(address owner, address spender) public view virtual override returns (uint256) {
- // Example: combine allowance from base ERC20 and permit
- uint256 baseAllowance = _getERC20Storage().allowances[owner][spender];
- // ... logic to consider permit allowances ...
- return baseAllowance;
- }
-
- // Helper to get ERC20 storage (example)
- function _getERC20Storage() internal view returns (ERC20Storage storage s) {
- uint256 slot = ERC20PermitMod.ERC20_STORAGE_POSITION;
- assembly {
- s := \`sload(slot)\`
- }
- }
-
- // Helper to get permit storage (example)
- function _getPermitStorage() internal view returns (ERC20PermitStorage storage s) {
- uint256 slot = ERC20PermitMod.ERC20_STORAGE_POSITION; // Assuming permit storage is within the same slot for this example
- assembly {
- s := \`sload(slot)\`
- }
- }
-}`}
-
-
-## Best Practices
-
-
-- Ensure the `permit` function in your facet correctly forwards the signature parameters (`v`, `r`, `s`) to the module's `permit` function.
-- Implement access control for the `permit` function if necessary, though typically it should be permissionless.
-- Be aware of the `DOMAIN_SEPARATOR` generated by the module and ensure it is correctly used when constructing signatures off-chain to prevent replay attacks.
-
-
## Integration Notes
-This module utilizes the diamond storage pattern, with its state managed at `ERC20_STORAGE_POSITION`. The `ERC20PermitStorage` struct is expected to be part of the overall storage layout at this position. Facets interacting with permit functionality should call the module's `permit` function, passing the necessary signed permit details. The module handles the validation of the signature against the `DOMAIN_SEPARATOR` and updates the allowance storage accordingly, which is then visible to any facet that can access the ERC20 storage.
+This module accesses shared diamond storage, so changes made through this module are immediately visible to facets using the same storage pattern. All functions are internal as per Compose conventions.
-
+
diff --git a/website/docs/library/token/ERC20/ERC20Permit/index.mdx b/website/docs/library/token/ERC20/ERC20Permit/index.mdx
index a71ec71f..9a426ce9 100644
--- a/website/docs/library/token/ERC20/ERC20Permit/index.mdx
+++ b/website/docs/library/token/ERC20/ERC20Permit/index.mdx
@@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx
index 6f21a29b..3dc5f436 100644
--- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx
+++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "ERC6909Facet"
-description: "Manages balances, allowances, and operator status for ERC-6909 tokens."
+description: "ERC-6909 fungible token implementation."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC6909/ERC6909/ERC6909Facet.sol"
---
@@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages balances, allowances, and operator status for ERC-6909 tokens.
+ERC-6909 fungible token implementation.
-- Implements ERC-6909 token standard for fungible and non-fungible tokens.
-- Supports standard balance, allowance, and operator management functions.
-- Allows for granular control over token transfers via `transfer` and `transferFrom`.
-- Enables setting `operator` permissions for streamlined multi-transaction operations.
+- Implements ERC-6909 standard for fungible tokens.
+- Supports individual token ID management for balances and approvals.
+- Includes explicit `setOperator` functionality for granular permissions.
## Overview
-The ERC6909Facet provides standard ERC-6909 functionality within a Compose diamond. It enables tracking token balances, managing allowances for token transfers, and setting operator permissions for accounts, facilitating composable token management.
+This facet implements the ERC-6909 standard for fungible tokens, enabling token transfers and approvals within a Compose diamond. It provides core functionalities for managing balances, allowances, and operator permissions for individual token IDs.
---
@@ -460,38 +459,19 @@ error ERC6909InvalidSpender(address _spender);
{`pragma solidity ^0.8.30;
-import {DiamondProxy} from "@compose/core/DiamondProxy.sol";
-import {ERC6909Facet} from "@compose/token/ERC6909/ERC6909/ERC6909Facet.sol";
+import { DiamondProxy } from "@compose/core/DiamondProxy.sol";
+import { IERC6909 } from "@compose/token/ERC6909/IERC6909.sol";
+import { ERC6909Facet } from "@compose/token/ERC6909/ERC6909/ERC6909Facet.sol";
contract MyDiamond is DiamondProxy {
- constructor(address _diamondAdmin, address[] memory _initFacets, bytes[] memory _calldatas)
- DiamondProxy(_diamondAdmin, _initFacets, _calldatas)
- {}
-
- function mintTokens(address _to, uint256 _id, uint256 _amount) public {
- // Assume minting logic is handled by another facet or initialization
- // For demonstration, we call a hypothetical internal function that would be part of the ERC6909Facet deployment
- // In a real scenario, minting might be exposed via a dedicated minting facet.
- // This example focuses on invoking ERC6909Facet functions post-deployment.
+ function transferERC6909(address _receiver, uint256 _id, uint256 _amount) external {
+ address erc6909Facet = getFacetAddress(ERC6909Facet.facetName());
+ IERC6909(erc6909Facet).transfer(_receiver, _id, _amount);
}
- function transferERC6909(address _receiver, uint256 _id, uint256 _amount) public {
- bytes4 selector = ERC6909Facet.transfer.selector;
- (bool success, ) = address(this).call(abi.encodeWithSelector(selector, _receiver, _id, _amount));
- require(success, "Transfer failed");
- }
-
- function approveERC6909(address _spender, uint256 _id, uint256 _amount) public {
- bytes4 selector = ERC6909Facet.approve.selector;
- (bool success, ) = address(this).call(abi.encodeWithSelector(selector, _spender, _id, _amount));
- require(success, "Approval failed");
- }
-
- function getBalance(address _owner, uint256 _id) public view returns (uint256) {
- bytes4 selector = ERC6909Facet.balanceOf.selector;
- (bool success, bytes memory data) = address(this).call(abi.encodeWithSelector(selector, _owner, _id));
- require(success, "Get balance failed");
- return abi.decode(data, (uint256));
+ function approveERC6909(address _spender, uint256 _id, uint256 _amount) external {
+ address erc6909Facet = getFacetAddress(ERC6909Facet.facetName());
+ IERC6909(erc6909Facet).approve(_spender, _id, _amount);
}
}`}
@@ -499,15 +479,15 @@ contract MyDiamond is DiamondProxy {
## Best Practices
-- Ensure the ERC6909Facet is initialized correctly during diamond deployment to set up necessary storage.
-- Manage operator permissions judiciously, granting them only to trusted addresses and revoking them when no longer needed.
-- Treat token IDs as critical state; ensure proper handling and prevent unintended minting or burning if such logic is exposed by other facets.
+- Initialize the ERC6909Storage struct correctly during deployment.
+- Ensure proper access control is layered on top of these functions if required by your diamond's architecture.
+- Leverage `allowance` and `balanceOf` to prevent reentrancy issues in dependent facets.
## Security Considerations
-Access control for functions like `transfer` and `approve` relies on the caller's permissions and token balances/allowances. Ensure that operator settings are managed securely to prevent unauthorized transfers. Input validation is crucial to prevent attacks such as integer overflows or transfers to zero addresses, handled by the facet's error codes. The `setOperator` function's approval mechanism should be carefully monitored.
+The `transfer` and `transferFrom` functions should be called with validated `_receiver` and `_sender` addresses. Ensure sufficient balance and allowance checks are performed by the caller or other facets to prevent `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` errors. Reentrancy is mitigated by checking balances/allowances before state changes and emitting events after successful operations.
@@ -551,4 +531,4 @@ Access control for functions like `transfer` and `approve` relies on the caller'
-
+
diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx
index 4b25731b..6e998aea 100644
--- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx
+++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "ERC6909Mod"
-description: "Provides minimal multi-token logic for ERC-6909"
+description: "Manages multi-token balances and approvals for ERC-6909."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC6909/ERC6909/ERC6909Mod.sol"
---
@@ -21,13 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Provides minimal multi-token logic for ERC-6909
+Manages multi-token balances and approvals for ERC-6909.
-- Provides internal functions for minting, burning, transferring, and approving ERC-6909 tokens.
-- Supports operator approvals for token management.
-- Leverages the diamond storage pattern for state management.
+- Supports minting, burning, and transferring of multiple token IDs with specified amounts.
+- Manages operator approvals for token transfers.
+- Emits standard ERC-6909 events for state changes.
+- Utilizes custom errors for gas-efficient error handling.
@@ -36,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module offers essential internal functions and storage for implementing the ERC-6909 standard. It enables composable, on-chain multi-token functionality within a diamond proxy, abstracting away the complexities of token management and allowing facets to integrate ERC-6909 capabilities seamlessly.
+This module provides the core logic for ERC-6909 multi-token functionality, enabling the management of various token IDs with distinct amounts. It handles essential operations like minting, burning, transferring, and approving token amounts, forming a foundational component for tokenized assets within a diamond.
---
@@ -477,33 +478,26 @@ error ERC6909InvalidSpender(address _spender);
{`pragma solidity ^0.8.30;
-import {IERC6909Mod} from "@compose/token/ERC6909/ERC6909/ERC6909Mod";
-
-contract MyTokenFacet {
- IERC6909Mod internal constant ERC6909 = IERC6909Mod(address(this));
-
- /**
- * @notice Mints tokens and approves a spender.
- * @param _to The recipient address.
- * @param _id The token ID.
- * @param _amount The amount to mint.
- * @param _spender The address to approve.
- */
- function mintAndApprove(address _to, uint256 _id, uint256 _amount, address _spender) external {
- ERC6909.mint(_to, _id, _amount);
- ERC6909.approve(_to, _spender, _id, _amount);
+import { IERC6909 } from "@compose/token/ERC6909/IERC6909.sol";
+import { ERC6909Mod } from "@compose/token/ERC6909/ERC6909/ERC6909Mod.sol";
+
+contract MyERC6909Facet {
+ using ERC6909Mod for ERC6909Mod.ERC6909Storage;
+
+ function mintToken(address _to, uint256 _id, uint256 _amount) external {
+ ERC6909Mod.mint(_to, _id, _amount);
+ }
+
+ function transferToken(address _from, address _to, uint256 _id, uint256 _amount) external {
+ ERC6909Mod.transfer(msg.sender, _from, _to, _id, _amount);
+ }
+
+ function approveToken(address _owner, address _spender, uint256 _id, uint256 _amount) external {
+ ERC6909Mod.approve(_owner, _spender, _id, _amount);
}
- /**
- * @notice Transfers tokens on behalf of another user.
- * @param _from The sender address.
- * @param _to The receiver address.
- * @param _id The token ID.
- * @param _amount The amount to transfer.
- */
- function transferFrom(address _from, address _to, uint256 _id, uint256 _amount) external {
- // Assuming _msgSender() is authorized to transferFrom
- ERC6909.transfer(_msgSender(), _from, _to, _id, _amount);
+ function burnToken(address _from, uint256 _id, uint256 _amount) external {
+ ERC6909Mod.burn(_from, _id, _amount);
}
}`}
@@ -511,15 +505,15 @@ contract MyTokenFacet {
## Best Practices
-- Implement access control within your facet to ensure only authorized entities can call mint, burn, or transfer functions.
-- Handle ERC6909 custom errors (`ERC6909InsufficientAllowance`, `ERC6909InsufficientBalance`, etc.) appropriately in your facet's logic to provide clear revert reasons.
-- When upgrading facets, ensure the storage layout defined by `ERC6909Storage` is maintained to preserve token state.
+- Ensure the `ERC6909Mod` facet is added to the diamond with the correct storage slot.
+- Implement access control within your facet functions to restrict who can call mint, burn, and transfer operations.
+- Handle custom errors like `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` in your facet's calling functions.
## Integration Notes
-This module relies on the diamond storage pattern to persist its state. The `ERC6909Storage` struct is located at a specific `STORAGE_POSITION` within the diamond's storage, identified by `keccak256("compose.erc6909")`. Facets using this module will interact with this storage slot via internal assembly calls within the module functions. Ensure that no other facets or modules attempt to write to this storage slot to maintain data integrity. The module functions are designed to be called internally by facets, ensuring that the underlying token logic is encapsulated and composable.
+The `ERC6909Mod` interacts with the diamond's storage via a dedicated slot identified by `keccak256("compose.erc6909")`. Facets using this module will typically import and use the `ERC6909Mod` library, applying its functions to the diamond's storage. The `ERC6909Storage` struct, though empty in this definition, serves as a placeholder for potential future state extensions within the diamond's storage layout for this module.
@@ -563,4 +557,4 @@ This module relies on the diamond storage pattern to persist its state. The `ERC
-
+
diff --git a/website/docs/library/token/ERC6909/ERC6909/index.mdx b/website/docs/library/token/ERC6909/ERC6909/index.mdx
index 52e76d1c..2f28b825 100644
--- a/website/docs/library/token/ERC6909/ERC6909/index.mdx
+++ b/website/docs/library/token/ERC6909/ERC6909/index.mdx
@@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx
index bc1b129a..4ce40f02 100644
--- a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx
+++ b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 3
title: "ERC721BurnFacet"
-description: "Burn ERC721 tokens within a Compose diamond."
+description: "Burns ERC-721 tokens within a diamond."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721BurnFacet.sol"
---
@@ -21,18 +21,17 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Burn ERC721 tokens within a Compose diamond.
+Burns ERC-721 tokens within a diamond.
-- Allows for the irreversible destruction of ERC721 tokens.
-- Updates internal token tracking and ownership records upon burning.
-- Integrates with the Compose diamond storage pattern for efficient state management.
+- Enables the permanent destruction of ERC-721 tokens.
+- Adheres to ERC-721 token lifecycle management.
## Overview
-The ERC721BurnFacet enables the destruction of ERC721 tokens directly within a Compose diamond. It allows for the permanent removal of tokens from circulation, updating associated ownership and approval records. This facet integrates seamlessly with the diamond's storage pattern for efficient token management.
+The ERC721BurnFacet enables the destruction of ERC-721 tokens directly within a Compose diamond. It provides the `burn` function for token holders or designated addresses to permanently remove a token from circulation, adhering to ERC-721 standards.
---
@@ -148,22 +147,42 @@ error ERC721InsufficientApproval(address _operator, uint256 _tokenId);
{`pragma solidity ^0.8.30;
-import { IDiamondCut } from "@compose/diamond/IDiamondCut.sol";
-import { IDiamondLoupe } from "@compose/diamond/IDiamondLoupe.sol";
+import {IDiamondCut} from "@compose/diamond/DiamondCutFacet.sol";
+import {IERC721Burn} from "@compose/token/ERC721/ERC721/IERC721Burn.sol";
+import {ERC721BurnFacet} from "@compose/token/ERC721/ERC721/ERC721BurnFacet.sol";
-import { ERC721BurnFacet } from "@compose/token/ERC721/ERC721/ERC721BurnFacet.sol";
+contract MyDiamond is IDiamondCut {
+ address constant ERC721_BURN_FACET_ADDRESS = address(0xYourERC721BurnFacetAddress);
-contract Deployer {
- function deploy() external {
- // Assume diamond contract is already deployed and cut
- address diamond = address(0x12345); // Replace with actual diamond address
+ function diamondCut(FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) external override {
+ // Implementation for diamondCut
+ }
+
+ function getFacetAddress(bytes4 _functionSelector) external view returns (address) {
+ // Implementation for getFacetAddress
+ return address(0);
+ }
- // Instantiate the facet contract
- ERC721BurnFacet burnFacet = ERC721BurnFacet(diamond);
+ function supportsInterface(bytes4 _interfaceId) external view returns (bool) {
+ // Implementation for supportsInterface
+ return false;
+ }
+}
+
+contract ExampleUsage {
+ IERC721Burn private erc721BurnFacet;
+
+ constructor(address diamondAddress) {
+ // Assuming diamondAddress is the address of your deployed diamond proxy
+ // and the ERC721BurnFacet is properly attached and routed.
+ erc721BurnFacet = IERC721Burn(diamondAddress);
+ }
- // Example: Burn a token
- // Note: Requires appropriate access control and approvals configured in the diamond
- // burnFacet.burn(1);
+ function burnToken(uint256 _tokenId) external {
+ /**
+ * @dev Burns the specified token. Requires appropriate approvals or ownership.
+ */
+ erc721BurnFacet.burn(_tokenId);
}
}`}
@@ -171,15 +190,14 @@ contract Deployer {
## Best Practices
-- Ensure the `ERC721BurnFacet` is correctly added to the diamond via `diamondCut` during deployment or upgrade.
-- Implement robust access control mechanisms within the diamond's `AccessControlFacet` or similar to restrict `burn` calls to authorized addresses.
-- Verify that the token ID being burned exists and that the caller has the necessary permissions (e.g., token owner or an approved operator) before invoking the `burn` function.
+- Ensure the `ERC721BurnFacet` is correctly cut into your diamond and its functions are properly routed.
+- Implement robust access control mechanisms within your diamond or related facets to manage who can call the `burn` function if direct ownership is not sufficient.
## Security Considerations
-The `burn` function should only be callable by authorized entities (e.g., the token owner or an approved operator). The diamond's access control layer must enforce this. The function does not handle reentrancy; however, as it modifies state and removes a token, careful consideration of the order of operations within the diamond's execution flow is advised.
+The `burn` function should be protected by appropriate access control to prevent unauthorized token destruction. Ensure that the caller has the necessary permissions (e.g., ownership of the token or sufficient approval) before invoking this function. The facet itself does not perform ownership checks, relying on the diamond's access control or other attached facets for this.
@@ -229,4 +247,4 @@ The `burn` function should only be callable by authorized entities (e.g., the to
-
+
diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx
index 14f436aa..f0db8a58 100644
--- a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx
+++ b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "ERC721Facet"
-description: "Manages ERC-721 compliant tokens within a Compose diamond."
+description: "Manages ERC-721 token ownership, transfers, and approvals."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721Facet.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages ERC-721 compliant tokens within a Compose diamond.
+Manages ERC-721 token ownership, transfers, and approvals.
-- Implements core ERC-721 functions: name, symbol, tokenURI, balanceOf, ownerOf, approvals, and transfers.
-- Supports both standard and safe transfer operations.
-- Leverages the diamond storage pattern for state management.
+- Implements core ERC-721 functionalities including ownership, approvals, and transfers.
+- Supports metadata retrieval via `tokenURI`.
+- Includes safe transfer mechanisms to ensure receiver compatibility.
## Overview
-The ERC721Facet provides standard ERC-721 functionality for managing non-fungible tokens. It allows querying token details, ownership, approvals, and performing transfers, all orchestrated through the diamond proxy.
+The ERC721Facet provides standard ERC-721 functionality for non-fungible tokens within a Compose diamond. It handles token metadata, ownership tracking, and approval mechanisms, enabling composable NFT contracts.
---
@@ -562,31 +562,32 @@ error ERC721InvalidOperator(address _operator);
{`pragma solidity ^0.8.30;
-import { IERC721Facet } from "@compose/token/ERC721/ERC721/IERC721Facet";
-import { DiamondProxy } from "@compose/diamond/DiamondProxy";
+import {DiamondDeploy} from "@compose/diamond/DiamondDeploy.sol";
+import {ERC721Facet} from "@compose/token/ERC721/ERC721/ERC721Facet.sol";
+import {IERC721} from "@compose/token/ERC721/IERC721.sol";
-contract ERC721Consumer {
- IERC721Facet private immutable _erc721Facet;
+contract MyNFtDiamond is DiamondDeploy {
+ function deploy() public {
+ address[] memory facets = new address[](1);
+ facets[0] = address(new ERC721Facet());
- constructor(address diamondProxyAddress) {
- _erc721Facet = IERC721Facet(diamondProxyAddress);
+ deployDiamond("MyNFTDiamond", facets);
}
- function getTokenName() external view returns (string memory) {
- return _erc721Facet.name();
+ function mintToken(address _to, uint256 _tokenId) public {
+ // Assuming minting logic is handled by another facet or external contract
+ // For demonstration, we'll call a hypothetical internal function if it existed
+ // In a real scenario, you'd interact with a facet that has minting capabilities.
}
- function getTokenSymbol() external view returns (string memory) {
- return _erc721Facet.symbol();
+ function getTokenOwner(uint256 _tokenId) public view returns (address) {
+ ERC721Facet erc721Facet = ERC721Facet(address(this));
+ return erc721Facet.ownerOf(_tokenId);
}
- function getTokenOwner(uint256 tokenId) external view returns (address) {
- return _erc721Facet.ownerOf(tokenId);
- }
-
- function transferToken(address to, uint256 tokenId) external {
- address from = _erc721Facet.ownerOf(tokenId); // Assuming caller is the owner
- _erc721Facet.transferFrom(from, to, tokenId);
+ function getTokenURI(uint256 _tokenId) public view returns (string memory) {
+ ERC721Facet erc721Facet = ERC721Facet(address(this));
+ return erc721Facet.tokenURI(_tokenId);
}
}`}
@@ -594,15 +595,15 @@ contract ERC721Consumer {
## Best Practices
-- Initialize the ERC721Facet with the correct storage slot and base URI during diamond deployment.
-- Ensure proper access control is configured at the diamond level for functions like `approve` and `transferFrom` if necessary, though many ERC-721 operations are permissionless for the token owner.
-- When upgrading, preserve the `STORAGE_POSITION` to maintain state continuity.
+- Initialize the ERC721Facet with a unique storage slot using `STORAGE_POSITION`.
+- Integrate with other facets for minting and burning functionalities, ensuring proper access control.
+- Use `internalTransferFrom` for internal state transitions and public transfer functions for external interactions.
## Security Considerations
-Input validation for token IDs and addresses is handled internally by the facet. Ensure that the diamond proxy correctly routes calls to this facet. Be mindful of potential reentrancy if custom logic interacts with token transfers outside of this facet's direct control. The `safeTransferFrom` functions include checks for receiver contract compatibility.
+Ensure that access control for minting and burning is handled by a separate, appropriately secured facet. Validate all input parameters for token IDs and addresses to prevent unintended state changes or denial-of-service attacks. Be mindful of reentrancy risks if other facets interact with this facet during transfer operations.
@@ -652,4 +653,4 @@ Input validation for token IDs and addresses is handled internally by the facet.
-
+
diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx
index e0fda3e3..d854699f 100644
--- a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx
+++ b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "ERC721Mod"
-description: "Internal logic for ERC-721 token management"
+description: "Manage ERC-721 tokens within a diamond proxy."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721Mod.sol"
---
@@ -21,14 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Internal logic for ERC-721 token management
+Manage ERC-721 tokens within a diamond proxy.
-- Provides essential ERC-721 functions: mint, burn, and transfer.
-- Manages ERC-721 state in a dedicated diamond storage slot for composability.
-- Emits standard `Transfer` events for on-chain tracking.
-- Includes specific custom errors for precise error reporting during operations.
+- Core ERC-721 operations: minting, burning, and transferring tokens.
+- Integrates with diamond storage using a predefined slot for ERC-721 state.
+- Employs specific custom errors for clear and gas-efficient error handling.
@@ -37,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides core ERC-721 functionality, including minting, burning, and transferring tokens. It leverages the diamond storage pattern to manage token state, making it composable with other facets. By encapsulating this logic, custom facets can easily integrate robust ERC-721 capabilities without reimplementing complex state management.
+This module provides essential internal logic for ERC-721 token management, including minting, burning, and transferring. It leverages the diamond storage pattern to store and access ERC-721 state, enabling custom facets to integrate robust token functionalities safely and composably.
---
@@ -316,42 +315,25 @@ error ERC721NonexistentToken(uint256 _tokenId);
{`pragma solidity ^0.8.30;
import {IERC721Mod} from "@compose/token/ERC721/ERC721/ERC721Mod";
-import {IERC721Storage} from "@compose/token/ERC721/ERC721/ERC721Mod";
+import {ERC721Storage} from "@compose/token/ERC721/ERC721/ERC721Mod";
contract MyERC721Facet {
- IERC721Mod public immutable erc721Mod;
+ IERC721Mod private immutable _erc721Mod;
constructor(address _diamondAddress) {
- // Assuming _diamondAddress is the address of the diamond proxy
- // and the ERC721Mod facet is correctly registered and accessible.
- erc721Mod = IERC721Mod(_diamondAddress);
+ _erc721Mod = IERC721Mod(_diamondAddress);
}
- /**
- * @notice Mints a new ERC-721 token.
- * @param _to The address to mint the token to.
- * @param _tokenId The ID of the token to mint.
- */
- function mintToken(address _to, uint256 _tokenId) external {
- erc721Mod.mint(_to, _tokenId);
+ function safeMint(address _to, uint256 _tokenId) external {
+ _erc721Mod.mint(_to, _tokenId);
}
- /**
- * @notice Transfers an ERC-721 token.
- * @param _from The current owner of the token.
- * @param _to The new owner of the token.
- * @param _tokenId The ID of the token to transfer.
- */
- function transferToken(address _from, address _to, uint256 _tokenId) external {
- erc721Mod.transferFrom(_from, _to, _tokenId);
+ function safeTransfer(address _from, address _to, uint256 _tokenId) external {
+ _erc721Mod.transferFrom(_from, _to, _tokenId);
}
- /**
- * @notice Burns an ERC-721 token.
- * @param _tokenId The ID of the token to burn.
- */
- function burnToken(uint256 _tokenId) external {
- erc721Mod.burn(_tokenId);
+ function safeBurn(uint256 _tokenId) external {
+ _erc721Mod.burn(_tokenId);
}
}`}
@@ -359,15 +341,15 @@ contract MyERC721Facet {
## Best Practices
-- Ensure the ERC721Mod facet is correctly registered with the diamond proxy before use.
-- Handle all custom errors emitted by the module (e.g., `ERC721IncorrectOwner`, `ERC721NonexistentToken`) in your facet's logic for robust error handling.
-- For state changes, always interact with the module via the diamond proxy address to ensure operations are routed correctly.
+- Call module functions with explicit checks for ownership and approvals before invocation to prevent unexpected reverts.
+- Handle specific `ERC721` errors (`ERC721IncorrectOwner`, `ERC721NonexistentToken`, etc.) to provide user-friendly feedback.
+- Ensure the `ERC721Storage` struct is correctly initialized and managed by the diamond's deployment process.
## Integration Notes
-This module utilizes the diamond storage pattern, storing its state at the `keccak256("compose.erc721")` slot. The `ERC721Storage` struct, containing `name`, `symbol`, and `baseURI`, is managed within this slot. Facets interacting with this module should be aware that all ERC-721 state modifications (minting, burning, transfers) are performed directly on this shared storage. The `getStorage()` function provides read-only access to this state via inline assembly.
+The `ERC721Mod` module interacts with diamond storage at the slot identified by `keccak256("compose.erc721")`. This slot holds an `ERC721Storage` struct containing `name`, `symbol`, and `baseURI`. Facets using this module will call its internal functions, which directly read from and write to this storage slot. Any facet that needs to access or modify ERC-721 state must be aware of this storage location and the `ERC721Storage` structure.
@@ -417,4 +399,4 @@ This module utilizes the diamond storage pattern, storing its state at the `kecc
-
+
diff --git a/website/docs/library/token/ERC721/ERC721/index.mdx b/website/docs/library/token/ERC721/ERC721/index.mdx
index 1cf14953..b426d100 100644
--- a/website/docs/library/token/ERC721/ERC721/index.mdx
+++ b/website/docs/library/token/ERC721/ERC721/index.mdx
@@ -14,21 +14,21 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx
index 93d1727f..8159f611 100644
--- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx
+++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 3
title: "ERC721EnumerableBurnFacet"
-description: "Burn ERC721 tokens and update enumeration"
+description: "Burn ERC721 tokens and maintain enumeration."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Burn ERC721 tokens and update enumeration
+Burn ERC721 tokens and maintain enumeration.
-- Burns ERC721 tokens, effectively destroying them.
-- Updates internal enumeration tracking to reflect token removal.
-- Utilizes the diamond proxy pattern for composability.
+- Enables burning of ERC721 tokens.
+- Maintains enumeration integrity after token destruction.
+- Integrates with existing ERC721 diamond standards.
## Overview
-This facet provides functionality to burn (destroy) ERC721 tokens within a Compose diamond. It ensures that token destruction is correctly reflected in the diamond's state, including any enumeration tracking mechanisms.
+The ERC721EnumerableBurnFacet allows for the destruction of ERC721 tokens while ensuring that the token's removal is correctly reflected in any enumeration tracking mechanisms managed by the diamond. This facet integrates seamlessly with other ERC721 facets, providing a complete token lifecycle management solution.
---
@@ -163,30 +163,24 @@ error ERC721InsufficientApproval(address _operator, uint256 _tokenId);
{`pragma solidity ^0.8.30;
-import { IDiamondWritable } from "@compose/diamond/IDiamondWritable.sol";
+import { IDiamondLoupe } from "@compose/diamond/IDiamondLoupe.sol";
+import { IERC721Enumerable } from "@compose/token/ERC721/ERC721Enumerable/IERC721Enumerable.sol";
import { ERC721EnumerableBurnFacet } from "@compose/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol";
contract Deployer {
address diamondAddress;
function deploy() public {
- // Assume diamond is deployed and facets are added
- diamondAddress = address(0x123...); // Replace with actual diamond address
- }
+ // Assume diamondAddress is set during diamond deployment
+ // ...
- function burnToken(uint256 _tokenId) public {
- address facetAddress = getAddressForFunction(diamondAddress, "burn(uint256)"); // Helper to get facet address
- ERC721EnumerableBurnFacet(facetAddress).burn(_tokenId);
- }
+ // Add the ERC721EnumerableBurnFacet to the diamond
+ // Facet address would be the deployed ERC721EnumerableBurnFacet contract
+ // address facetAddress = address(new ERC721EnumerableBurnFacet());
+ // IDiamond.addFacet(diamondAddress, facetAddress);
- // This is a placeholder and would need actual implementation
- function getAddressForFunction(address _diamond, string memory _functionSelector) internal pure returns (address) {
- // In a real scenario, this would query the diamond's facet address mapping.
- // For this example, we assume a known facet address for demonstration.
- if (keccak256(abi.encodePacked(_functionSelector)) == keccak256(abi.encodePacked("burn(uint256)"))) {
- return 0x456...; // Replace with actual ERC721EnumerableBurnFacet address
- }
- revert("Function not found");
+ // Example of burning a token (replace with actual diamond address)
+ // IERC721Enumerable(diamondAddress).burn(123);
}
}`}
@@ -194,15 +188,15 @@ contract Deployer {
## Best Practices
-- Ensure the `ERC721EnumerableBurnFacet` is added to the diamond with the correct selector for the `burn` function.
-- Call `burn` via the diamond proxy to ensure proper function routing and access control.
-- Be aware that burning a token is irreversible and will remove it from any enumeration tracking.
+- Ensure the ERC721EnumerableBurnFacet is added to the diamond proxy during deployment or upgrade.
+- Call the `burn` function through the diamond proxy address to ensure proper function routing.
+- This facet's storage is managed by the diamond; do not interact with its internal storage directly.
## Security Considerations
-The `burn` function should be protected by appropriate access control mechanisms (e.g., ownership or operator approval) to prevent unauthorized token destruction. Ensure that the `ERC721NonexistentToken` and `ERC721InsufficientApproval` errors are handled by the caller to prevent unexpected reverts.
+The `burn` function requires the caller to be the owner of the token or approved by the owner. The facet emits a `Transfer` event for burned tokens, indicating a transfer from the owner to the zero address. Ensure appropriate access control is enforced by the diamond proxy for token transfers and approvals prior to calling `burn`.
@@ -252,4 +246,4 @@ The `burn` function should be protected by appropriate access control mechanisms
-
+
diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx
index 8844313e..86e87230 100644
--- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx
+++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "ERC721EnumerableFacet"
-description: "Manages ERC721 token metadata and enumerable features."
+description: "Manages ERC721 tokens with enumerable properties."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol"
---
@@ -21,19 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages ERC721 token metadata and enumerable features.
+Manages ERC721 tokens with enumerable properties.
-- Implements ERC721 standard metadata and enumerable properties.
-- Provides efficient `tokenOfOwnerByIndex` for indexed token retrieval.
-- Supports `approve` and `setApprovalForAll` for granular token permissions.
-- Includes safe transfer variants for enhanced security with receiver contracts.
+- Implements ERC721 standard with enumerable extensions.
+- Supports standard ERC721 metadata and ownership queries.
+- Provides functions for token approvals and transfers, including safe transfers.
+- Enables querying of token ownership by index for a given address.
## Overview
-The ERC721EnumerableFacet provides standard ERC721 functionality, including token metadata and ownership tracking. It enables efficient querying of token balances and ownership by index, crucial for applications requiring ordered token management within a Compose diamond.
+The ERC721EnumerableFacet provides standard ERC721 functionality alongside enumerable capabilities. It allows querying token metadata, ownership, and provides functions for approvals and transfers, adhering to the ERC721 standard while enabling efficient iteration over owned tokens.
---
@@ -637,31 +637,34 @@ error ERC721OutOfBoundsIndex(address _owner, uint256 _index);
{`pragma solidity ^0.8.30;
-import { DiamondInit } from "@compose/diamond-core/DiamondInit.sol";
-import { DiamondCut } from "@compose/diamond-core/DiamondCut.sol";
+import { IDiamondCut } from "@compose/diamond/Diamond.sol";
import { ERC721EnumerableFacet } from "@compose/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol";
-contract Deployer {
- function deploy() external {
- // Assume diamond contract is already deployed and initialized
- address diamond = address(0x123); // Replace with actual diamond address
-
- // Prepare facet cut data
- bytes memory initData = abi.encodeWithSelector(ERC721EnumerableFacet.init.selector, "My Token", "MTK");
- ERC721EnumerableFacet.FacetCut[] memory cuts = new ERC721EnumerableFacet.FacetCut[](1);
- cuts[0] = ERC721EnumerableFacet.FacetCut({
- facetAddress: address(new ERC721EnumerableFacet()), // Replace with actual deployed facet address
- action: DiamondCut.Action.ADD,
- selectors: ERC721EnumerableFacet.getSelectors()
- });
-
- // Execute cut
- DiamondCut(diamond).diamondCut(cuts, address(0), initData);
-
- // Example of calling functions after deployment
- ERC721EnumerableFacet(diamond).name();
- ERC721EnumerableFacet(diamond).symbol();
- ERC721EnumerableFacet(diamond).totalSupply();
+contract MyDiamond is IDiamondCut {
+ address constant ERC721_ENUMERABLE_FACET_CUT = address(0); // Placeholder
+
+ function diamondCut(FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) external override {}
+
+ function diamondCut() public view returns (FacetCut[] memory) {
+ // Example of how to retrieve data
+ ERC721EnumerableFacet enumerableFacet = ERC721EnumerableFacet(address(this));
+
+ // Get token name
+ string memory tokenName = enumerableFacet.name();
+
+ // Get total supply
+ uint256 supply = enumerableFacet.totalSupply();
+
+ // Get owner of a token
+ address owner = enumerableFacet.ownerOf(1);
+
+ // Approve another address to transfer token 1
+ enumerableFacet.approve(msg.sender, 1);
+
+ // Transfer token 1 to msg.sender
+ enumerableFacet.transferFrom(owner, msg.sender, 1);
+
+ return new FacetCut[](0); // Placeholder
}
}`}
@@ -669,15 +672,15 @@ contract Deployer {
## Best Practices
-- Initialize the facet with the desired token name and symbol using its `init` function during diamond deployment.
-- Utilize `tokenOfOwnerByIndex` for iterating through a user's tokens, ensuring proper handling of index bounds.
-- Leverage internal functions like `internalTransferFrom` within other facets for robust token management.
+- Initialize the facet with core ERC721 metadata (name, symbol, baseURI) during diamond deployment.
+- Use `ownerOf` and `tokenOfOwnerByIndex` together for iterating through a user's tokens, respecting gas limits.
+- Ensure correct access control is implemented at the diamond level for functions like `approve` and `transferFrom`.
## Security Considerations
-Ensure proper access control is implemented at the diamond proxy level for functions that modify state (e.g., `approve`, `setApprovalForAll`). Validate `_to` and `_from` addresses in transfer functions to prevent unintended transfers. Be aware of potential reentrancy risks if external calls are made within transfer functions, though this facet's core transfer logic is internal.
+Input validation for token IDs and addresses is critical. Ensure proper checks are in place to prevent reentrancy for transfer functions. Access control for `approve` and `transferFrom` should be managed at the diamond proxy level to restrict unauthorized operations.
@@ -727,4 +730,4 @@ Ensure proper access control is implemented at the diamond proxy level for funct
-
+
diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx
index 94ff8568..4c15eaf9 100644
--- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx
+++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "ERC721EnumerableMod"
-description: "Manages enumerable ERC-721 token state and operations."
+description: "Manages enumerable ERC721 tokens within a diamond."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol"
---
@@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages enumerable ERC-721 token state and operations.
+Manages enumerable ERC721 tokens within a diamond.
-- Manages internal state for enumerable ERC-721 tokens.
-- Provides atomic mint, burn, and transfer operations that update enumeration lists.
-- Reverts with specific errors for invalid operations such as non-existent tokens or incorrect ownership.
+- Manages token enumeration order, crucial for ordered list functionality.
+- Integrates seamlessly with the diamond storage pattern using a predefined storage slot.
+- Provides internal logic for minting, burning, and transferring tokens, abstracting away enumeration details.
@@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides the core internal logic for managing enumerable ERC-721 tokens within a Compose diamond. It ensures that token ownership and enumeration order are consistently maintained across mint, burn, and transfer operations. By abstracting this complex state management, facets can integrate ERC-721 functionality safely and efficiently.
+This module provides the core logic for managing enumerable ERC721 tokens, ensuring tokens are tracked in a consistent order. It integrates with the diamond storage pattern to maintain state across facets, enabling composable and upgradeable ERC721 functionality without relying on inheritance.
---
@@ -298,45 +298,47 @@ error ERC721NonexistentToken(uint256 _tokenId);
{`pragma solidity ^0.8.30;
import {ERC721EnumerableMod} from "@compose/token/ERC721/ERC721Enumerable/ERC721EnumerableMod";
+import {IERC721Enumerable} from "@compose/token/ERC721/IERC721Enumerable";
-contract MyERC721Facet {
- ERC721EnumerableMod private enumerableMod;
+contract MyERC721Facet is IERC721Enumerable {
+ ERC721EnumerableMod internal enumerableMod;
constructor(address _diamondAddress) {
- // Assuming ERC721EnumerableMod is deployed and accessible
- // via the diamond proxy mechanism.
enumerableMod = ERC721EnumerableMod(_diamondAddress);
}
- function mintToken(address _to, uint256 _tokenId) external {
+ function mint(address _to, uint256 _tokenId) external override {
enumerableMod.mint(_to, _tokenId);
+ emit Transfer(address(0), _to, _tokenId);
}
- function transferToken(address _from, address _to, uint256 _tokenId) external {
- // In a real scenario, you'd check ownership and approvals here
- // before calling transferFrom.
- enumerableMod.transferFrom(_from, _to, _tokenId, msg.sender);
+ function burn(uint256 _tokenId) external override {
+ address owner = OwnerFacet(_diamondAddress).ownerOf(_tokenId);
+ enumerableMod.burn(_tokenId, msg.sender);
+ emit Transfer(owner, address(0), _tokenId);
}
- function burnToken(uint256 _tokenId) external {
- // In a real scenario, you'd check ownership before calling burn.
- enumerableMod.burn(_tokenId, msg.sender);
+ function transferFrom(address _from, address _to, uint256 _tokenId) external override {
+ enumerableMod.transferFrom(_from, _to, _tokenId, msg.sender);
+ emit Transfer(_from, _to, _tokenId);
}
+
+ // ... other ERC721 functions ...
}`}
## Best Practices
-- Always ensure the calling facet has the necessary permissions before invoking mint, burn, or transfer functions.
-- Handle custom errors like `ERC721IncorrectOwner`, `ERC721NonexistentToken`, and `ERC721InvalidReceiver` to provide clear feedback to users.
-- When upgrading facets, ensure the storage layout defined by `ERC721EnumerableStorage` remains compatible.
+- Ensure `mint` and `burn` functions correctly emit the `Transfer` event as required by the ERC721 standard and the module's internal logic.
+- Always validate ownership and approvals before calling `transferFrom` or `burn` if implementing custom access control logic in your facet.
+- Handle potential reverts from the module's functions (`ERC721IncorrectOwner`, `ERC721NonexistentToken`, etc.) by providing clear error messages to the end-user.
## Integration Notes
-This module utilizes the Compose diamond storage pattern. Its state is stored at a specific slot identified by `keccak256("compose.erc721.enumerable")`. Facets interacting with this module will access the `ERC721EnumerableStorage` struct via the `getStorage()` function, which returns a pointer to the storage slot. Any modifications made through the module's functions (mint, burn, transferFrom) directly alter this shared storage, making the changes visible to all facets that read from the same storage slot.
+The `ERC721EnumerableMod` stores its state in a dedicated slot within the diamond's storage, identified by `keccak256("compose.erc721.enumerable")`. Facets interacting with this module must retrieve an instance of the module using its address and call its internal functions. The `ERC721EnumerableStorage` struct, though defined with placeholder fields (`name`, `symbol`, `baseURI`), is primarily used to anchor the storage slot. Custom facets are responsible for implementing the external-facing ERC721 interface and emitting events. The module relies on the diamond's owner or other authorized facets to enforce access control for `mint`, `burn`, and `transferFrom` operations.
@@ -380,4 +382,4 @@ This module utilizes the Compose diamond storage pattern. Its state is stored at
-
+
diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx
index 95ccf35f..9eb555e1 100644
--- a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx
+++ b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx
@@ -14,21 +14,21 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx
index 6c1cac83..6853c960 100644
--- a/website/docs/library/token/Royalty/RoyaltyFacet.mdx
+++ b/website/docs/library/token/Royalty/RoyaltyFacet.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 2
title: "RoyaltyFacet"
-description: "Handles ERC-2981 royalty information for NFTs."
+description: "Handles ERC-2981 royalty information for tokens."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/Royalty/RoyaltyFacet.sol"
---
@@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Handles ERC-2981 royalty information for NFTs.
+Handles ERC-2981 royalty information for tokens.
-- Implements ERC-2981 `royaltyInfo` function.
-- Supports default royalties and token-specific overrides.
-- Calculates royalty amount as a percentage of sale price using basis points.
+- Implements the ERC-2981 standard for on-chain royalty payments.
+- Supports token-specific royalty configurations.
+- Calculates royalty amounts as a percentage of the sale price using basis points.
## Overview
-The RoyaltyFacet implements the ERC-2981 standard, enabling diamonds to return royalty information for a given token ID and sale price. It supports both default royalties and token-specific overrides, ensuring creators receive their due.
+The RoyaltyFacet implements the ERC-2981 standard, enabling the diamond to return royalty information for a given token ID and sale price. It supports both default royalties and token-specific overrides, ensuring creators receive their due.
---
@@ -129,33 +129,36 @@ Returns royalty information for a given token and sale price. Returns token-spec
{`pragma solidity ^0.8.30;
-import {IDiamondCut} from "@compose/diamond/Diamond.sol";
-import {IDiamondLoupe} from "@compose/diamond/Diamond.sol";
-import {RoyaltyFacet} from "@compose/token/Royalty/RoyaltyFacet.sol";
+import { IRoyaltyFacet } from "@compose/token/Royalty/IRoyaltyFacet";
+import { RoyaltyStorage } from "@compose/token/Royalty/RoyaltyStorage";
-contract MyDiamond is IDiamondCut, IDiamondLoupe {
- // ... diamond deployment and initialization ...
+contract RoyaltyConsumer {
+ address immutable diamondAddress;
- function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address, uint256) {
- // Delegate to the RoyaltyFacet
- return RoyaltyFacet(address(this)).royaltyInfo(_tokenId, _salePrice);
+ constructor(address _diamondAddress) {
+ diamondAddress = _diamondAddress;
}
- // ... other diamond functions ...
+ function getRoyaltyDetails(uint256 tokenId, uint256 salePrice) external view returns (address, uint256) {
+ IRoyaltyFacet royaltyFacet = IRoyaltyFacet(diamondAddress);
+ (address receiver, uint256 royaltyAmount) = royaltyFacet.royaltyInfo(tokenId, salePrice);
+ return (receiver, royaltyAmount);
+ }
}`}
## Best Practices
-- Initialize the `RoyaltyStorage` struct with default royalty recipient and basis points during diamond deployment.
-- Ensure the `RoyaltyFacet` is added to the diamond with appropriate selectors.
+- Initialize the RoyaltyStorage struct with default royalty information during diamond deployment or upgrade.
+- Ensure the `royaltyInfo` function is correctly implemented to calculate royalties based on sale price and basis points.
+- Integrate this facet with token facets (e.g., ERC1155Facet) to enable royalty distribution.
## Security Considerations
-Access control for setting default royalties should be managed at the diamond level. Ensure sale price and royalty basis points are validated to prevent unexpected calculations.
+The `royaltyInfo` function is `view`, meaning it does not modify state and is safe from reentrancy. Ensure that default and token-specific royalty configurations are set correctly to prevent unexpected royalty distributions.
@@ -205,4 +208,4 @@ Access control for setting default royalties should be managed at the diamond le
-
+
diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx
index 3720849c..0a358872 100644
--- a/website/docs/library/token/Royalty/RoyaltyMod.mdx
+++ b/website/docs/library/token/Royalty/RoyaltyMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "RoyaltyMod"
-description: "Manages ERC-2981 royalty information for tokens."
+description: "Handles ERC-2981 royalty logic for tokens"
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/Royalty/RoyaltyMod.sol"
---
@@ -21,13 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Manages ERC-2981 royalty information for tokens.
+Handles ERC-2981 royalty logic for tokens
-- Implements ERC-2981 `royaltyInfo` function logic.
-- Supports both default royalties and token-specific royalty overrides.
-- Provides functions to set, delete, and query royalty information.
+- Implements ERC-2981 standard for royalty information.
+- Supports setting both default and token-specific royalties.
+- Allows resetting token-specific royalties to fall back to defaults.
+- Provides functions to query royalty information based on token ID and sale price.
@@ -36,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This module provides the core logic and storage for ERC-2981 royalty standards. It enables setting and querying both default and token-specific royalties, ensuring compliance and composability for token contracts within a diamond.
+This module provides the core logic for ERC-2981 royalty payments, enabling tokens within a diamond to support royalty distributions. It manages both default royalties for all tokens and specific royalties for individual token IDs, ensuring compliance with the ERC-2981 standard.
---
@@ -299,21 +300,19 @@ error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver);
{`pragma solidity ^0.8.30;
-import {IRoyaltyMod} from "@compose/token/Royalty/RoyaltyMod";
+import {IRoyaltyFacet} from "@compose/token/Royalty/IRoyaltyFacet";
+import {RoyaltyMod} from "@compose/token/Royalty/RoyaltyMod";
-contract MyRoyaltyFacet {
- address immutable DIAMOND_STORAGE_ADDRESS;
+contract MyTokenFacet {
+ using RoyaltyMod for RoyaltyMod;
- constructor(address _diamondStorageAddress) {
- DIAMOND_STORAGE_ADDRESS = _diamondStorageAddress;
+ function setMyTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) external {
+ // Assuming access control is handled elsewhere or by the diamond proxy
+ RoyaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeNumerator);
}
- function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) external {
- IRoyaltyMod(DIAMOND_STORAGE_ADDRESS).setTokenRoyalty(_tokenId, _receiver, _feeNumerator);
- }
-
- function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address, uint256) {
- return IRoyaltyMod(DIAMOND_STORAGE_ADDRESS).royaltyInfo(_tokenId, _salePrice);
+ function getMyTokenRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address, uint256) {
+ return RoyaltyMod.royaltyInfo(_tokenId, _salePrice);
}
}`}
@@ -321,15 +320,15 @@ contract MyRoyaltyFacet {
## Best Practices
-- Use `setDefaultRoyalty` and `setTokenRoyalty` with validated receiver addresses and fee numerators to prevent errors.
-- Handle custom errors like `ERC2981InvalidDefaultRoyalty` and `ERC2981InvalidTokenRoyalty` in calling facets.
-- Leverage `resetTokenRoyalty` to revert token-specific royalties to the default setting.
+- Ensure appropriate access control is implemented at the facet or diamond level before calling `setDefaultRoyalty` or `setTokenRoyalty`.
+- Handle the custom errors (`ERC2981InvalidDefaultRoyalty`, `ERC2981InvalidTokenRoyalty`, etc.) to provide clear feedback to users.
+- Be mindful of storage slot usage; this module reserves `keccak256("compose.erc2981")` for its storage.
## Integration Notes
-The RoyaltyMod utilizes a dedicated storage slot identified by `keccak256("compose.erc2981")` to store its `RoyaltyStorage` struct. This struct contains default royalty information. Token-specific royalties are stored within the `tokenRoyalty` mapping within the `RoyaltyStorage` struct itself, managed by the module. Facets interacting with this module should use the `IRoyaltyMod` interface and call functions via the diamond proxy, which dispatches to the appropriate facet containing this module's logic.
+The `RoyaltyMod` module manages its state within the diamond's storage at the slot identified by `keccak256("compose.erc2981")`. This slot holds a `RoyaltyStorage` struct, which contains the `defaultRoyaltyInfo`. Facets interacting with this module can access this storage directly via `RoyaltyMod.getStorage()` or indirectly through the module's functions. Changes to default royalties are immediately reflected in `royaltyInfo` calls for tokens without specific royalty settings. Token-specific royalties override default settings.
@@ -379,4 +378,4 @@ The RoyaltyMod utilizes a dedicated storage slot identified by `keccak256("compo
-
+
diff --git a/website/docs/library/token/Royalty/index.mdx b/website/docs/library/token/Royalty/index.mdx
index 0ebae10a..e0f3e108 100644
--- a/website/docs/library/token/Royalty/index.mdx
+++ b/website/docs/library/token/Royalty/index.mdx
@@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"
/>
}
size="medium"
diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx
index 47fd8fda..06742b4c 100644
--- a/website/docs/library/utils/NonReentrancyMod.mdx
+++ b/website/docs/library/utils/NonReentrancyMod.mdx
@@ -1,7 +1,7 @@
---
sidebar_position: 1
title: "NonReentrancyMod"
-description: "Prevent reentrant function calls within facets."
+description: "Prevent reentrant function calls in facets."
gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/libraries/NonReentrancyMod.sol"
---
@@ -21,13 +21,12 @@ import GradientText from '@site/src/components/ui/GradientText';
import GradientButton from '@site/src/components/ui/GradientButton';
-Prevent reentrant function calls within facets.
+Prevent reentrant function calls in facets.
-- Prevents reentrant calls to functions, enhancing security.
-- Uses a simple, composable library interface for easy integration.
-- Emits a `Reentrancy` error if a reentrant call is detected.
+- Prevents reentrant function calls using a simple lock mechanism.
+- Employs a custom error `Reentrancy` for explicit failure conditions.
@@ -36,7 +35,7 @@ This module provides internal functions for use in your custom facets. Import it
## Overview
-This library provides a robust mechanism to prevent reentrant function calls, a common vulnerability. By incorporating its `enter` and `exit` functions, facets can ensure that sensitive operations are executed atomically, safeguarding against unexpected state changes and exploits.
+The NonReentrancyMod library provides essential functions to prevent reentrant calls within your diamond facets. By implementing its `enter` and `exit` functions, you ensure that a function cannot be re-entered before its initial execution completes, safeguarding against common reentrancy vulnerabilities.
---
@@ -99,26 +98,19 @@ error Reentrancy();
import {NonReentrancyMod} from "@compose/libraries/NonReentrancyMod";
contract MyFacet {
- using NonReentrancyMod for uint256; // Example: using with a dummy type
-
- function sensitiveOperation() external {
- // Enter the non-reentrant guard
- NonReentrancyMod.enter();
-
- try {
- // Perform sensitive operations here
- // ...
- } finally {
- // Exit the non-reentrant guard
- NonReentrancyMod.exit();
- }
- }
+ using NonReentrancyMod for NonReentrancyMod.State;
+
+ NonReentrancyMod.State private _nonReentrancyState;
+
+ /**
+ * @notice Demonstrates a non-reentrant function.
+ */
+ function protectedFunction() external {
+ _nonReentrancyState.enter(); // Lock the function
+
+ // ... perform critical operations ...
- // Example of direct usage without try/finally
- function anotherSensitiveOperation() external {
- NonReentrancyMod.enter();
- // ... operations ...
- NonReentrancyMod.exit();
+ _nonReentrancyState.exit(); // Unlock the function
}
}`}
@@ -126,18 +118,18 @@ contract MyFacet {
## Best Practices
-- Always pair `NonReentrancyMod.enter()` with `NonReentrancyMod.exit()` within a `try...finally` block to guarantee exit, even if errors occur during execution.
-- Ensure `NonReentrancyMod.exit()` is called before any external calls or state-changing operations that might re-enter the current function or other protected functions.
+- Always call `enter()` at the beginning of a function and `exit()` at the end to properly guard against reentrancy.
+- Utilize the `Reentrancy` custom error for clear and gas-efficient error reporting if a reentrant call is detected.
## Integration Notes
-The `NonReentrancyMod` is a library and does not directly interact with diamond storage. Its state is managed internally within the scope of the calling facet's execution context. Facets can integrate this library by importing it and calling its `enter` and `exit` functions directly. No specific storage slots are required from the diamond's perspective for this library's operation.
+The NonReentrancyMod library does not require direct integration with diamond storage. Its state is managed internally within the facet that uses it. Facets should declare an internal state variable of type `NonReentrancyMod.State` and use the library's functions to manage reentrancy protection.
-
+
diff --git a/website/docs/library/utils/index.mdx b/website/docs/library/utils/index.mdx
index 67e190d0..5ad7301d 100644
--- a/website/docs/library/utils/index.mdx
+++ b/website/docs/library/utils/index.mdx
@@ -14,7 +14,7 @@ import Icon from '@site/src/components/ui/Icon';
}
size="medium"