From 5547af7648d4e94349b9c22678c22e5445d5012b Mon Sep 17 00:00:00 2001 From: ogazboiz Date: Mon, 27 Oct 2025 06:35:48 +0100 Subject: [PATCH 1/4] Implement ERC-165 facet and library for interface detection --- src/diamond/ERC165Facet.sol | 19 ++++++++++++++ src/interfaces/IERC165.sol | 15 +++++++++++ src/libraries/LibERC165.sol | 52 +++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 src/diamond/ERC165Facet.sol create mode 100644 src/interfaces/IERC165.sol create mode 100644 src/libraries/LibERC165.sol diff --git a/src/diamond/ERC165Facet.sol b/src/diamond/ERC165Facet.sol new file mode 100644 index 00000000..0ca5e088 --- /dev/null +++ b/src/diamond/ERC165Facet.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.30; + +import {IERC165} from "../interfaces/IERC165.sol"; +import {LibERC165} from "../libraries/LibERC165.sol"; + +/// @title ERC165Facet — ERC-165 Standard Interface Detection Facet +/// @notice Facet implementation of ERC-165 for diamond proxy pattern +/// @dev Allows querying which interfaces are implemented by the diamond +contract ERC165Facet is IERC165 { + /// @notice Query if a contract implements an interface + /// @param _interfaceId The interface identifier, as specified in ERC-165 + /// @dev This function checks if the diamond supports the given interface ID + /// @return `true` if the contract implements `_interfaceId` and + /// `_interfaceId` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 _interfaceId) external view override returns (bool) { + return LibERC165.supportsInterface(_interfaceId); + } +} diff --git a/src/interfaces/IERC165.sol b/src/interfaces/IERC165.sol new file mode 100644 index 00000000..6ed2e484 --- /dev/null +++ b/src/interfaces/IERC165.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.30; + +/// @title ERC-165 Standard Interface Detection Interface +/// @notice Interface for detecting what interfaces a contract implements +/// @dev ERC-165 allows contracts to publish their supported interfaces +interface IERC165 { + /// @notice Query if a contract implements an interface + /// @param _interfaceId The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `_interfaceId` and + /// `_interfaceId` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 _interfaceId) external view returns (bool); +} diff --git a/src/libraries/LibERC165.sol b/src/libraries/LibERC165.sol new file mode 100644 index 00000000..897427ad --- /dev/null +++ b/src/libraries/LibERC165.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.30; + +import {IERC165} from "../interfaces/IERC165.sol"; + +/// @title LibERC165 — ERC-165 Standard Interface Detection Library +/// @notice Provides internal functions and storage layout for ERC-165 interface detection. +/// @dev Uses ERC-8042 for storage location standardization +library LibERC165 { + /// @notice Storage slot identifier, defined using keccak256 hash of the library diamond storage identifier. + bytes32 constant STORAGE_POSITION = keccak256("compose.erc165"); + + /// @notice ERC-165 storage layout using the ERC-8042 standard. + /// @custom:storage-location erc8042:compose.erc165 + struct ERC165Storage { + /// @notice Mapping of interface IDs to whether they are supported + mapping(bytes4 => bool) supportedInterfaces; + } + + /// @notice Returns a pointer to the ERC-165 storage struct. + /// @dev Uses inline assembly to bind the storage struct to the fixed storage position. + /// @return s The ERC-165 storage struct. + function getStorage() internal pure returns (ERC165Storage storage s) { + bytes32 position = STORAGE_POSITION; + assembly { + s.slot := position + } + } + + /// @notice Register that a contract supports an interface + /// @param _interfaceId The interface ID to register + /// @dev Call this function during initialization to register supported interfaces. + /// For example, in an ERC721 facet initialization, you would call: + /// `LibERC165.registerInterface(type(IERC721).interfaceId)` + function registerInterface(bytes4 _interfaceId) internal { + ERC165Storage storage s = getStorage(); + s.supportedInterfaces[_interfaceId] = true; + } + + /// @notice Check if a contract supports an interface + /// @param _interfaceId The interface ID to check + /// @return True if the interface is supported, false otherwise + function supportsInterface(bytes4 _interfaceId) internal view returns (bool) { + ERC165Storage storage s = getStorage(); + // If the ERC165 interface itself is being queried, return true + // since this library implements ERC165 + if (_interfaceId == type(IERC165).interfaceId) { + return true; + } + return s.supportedInterfaces[_interfaceId]; + } +} From 9894476484ee37df4eac51e0a37873bde03f4430 Mon Sep 17 00:00:00 2001 From: ogazboiz Date: Mon, 27 Oct 2025 06:35:48 +0100 Subject: [PATCH 2/4] Implement ERC-165 facet and library for interface detection --- src/diamond/ERC165Facet.sol | 18 +++++++++++++ src/interfaces/IERC165.sol | 15 +++++++++++ src/libraries/LibERC165.sol | 52 +++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 src/diamond/ERC165Facet.sol create mode 100644 src/interfaces/IERC165.sol create mode 100644 src/libraries/LibERC165.sol diff --git a/src/diamond/ERC165Facet.sol b/src/diamond/ERC165Facet.sol new file mode 100644 index 00000000..773285b5 --- /dev/null +++ b/src/diamond/ERC165Facet.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.30; + +import {LibERC165} from "../libraries/LibERC165.sol"; + +/// @title ERC165Facet — ERC-165 Standard Interface Detection Facet +/// @notice Facet implementation of ERC-165 for diamond proxy pattern +/// @dev Allows querying which interfaces are implemented by the diamond +contract ERC165Facet { + /// @notice Query if a contract implements an interface + /// @param _interfaceId The interface identifier, as specified in ERC-165 + /// @dev This function checks if the diamond supports the given interface ID + /// @return `true` if the contract implements `_interfaceId` and + /// `_interfaceId` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 _interfaceId) external view returns (bool) { + return LibERC165.supportsInterface(_interfaceId); + } +} diff --git a/src/interfaces/IERC165.sol b/src/interfaces/IERC165.sol new file mode 100644 index 00000000..6ed2e484 --- /dev/null +++ b/src/interfaces/IERC165.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.30; + +/// @title ERC-165 Standard Interface Detection Interface +/// @notice Interface for detecting what interfaces a contract implements +/// @dev ERC-165 allows contracts to publish their supported interfaces +interface IERC165 { + /// @notice Query if a contract implements an interface + /// @param _interfaceId The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `_interfaceId` and + /// `_interfaceId` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 _interfaceId) external view returns (bool); +} diff --git a/src/libraries/LibERC165.sol b/src/libraries/LibERC165.sol new file mode 100644 index 00000000..897427ad --- /dev/null +++ b/src/libraries/LibERC165.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.30; + +import {IERC165} from "../interfaces/IERC165.sol"; + +/// @title LibERC165 — ERC-165 Standard Interface Detection Library +/// @notice Provides internal functions and storage layout for ERC-165 interface detection. +/// @dev Uses ERC-8042 for storage location standardization +library LibERC165 { + /// @notice Storage slot identifier, defined using keccak256 hash of the library diamond storage identifier. + bytes32 constant STORAGE_POSITION = keccak256("compose.erc165"); + + /// @notice ERC-165 storage layout using the ERC-8042 standard. + /// @custom:storage-location erc8042:compose.erc165 + struct ERC165Storage { + /// @notice Mapping of interface IDs to whether they are supported + mapping(bytes4 => bool) supportedInterfaces; + } + + /// @notice Returns a pointer to the ERC-165 storage struct. + /// @dev Uses inline assembly to bind the storage struct to the fixed storage position. + /// @return s The ERC-165 storage struct. + function getStorage() internal pure returns (ERC165Storage storage s) { + bytes32 position = STORAGE_POSITION; + assembly { + s.slot := position + } + } + + /// @notice Register that a contract supports an interface + /// @param _interfaceId The interface ID to register + /// @dev Call this function during initialization to register supported interfaces. + /// For example, in an ERC721 facet initialization, you would call: + /// `LibERC165.registerInterface(type(IERC721).interfaceId)` + function registerInterface(bytes4 _interfaceId) internal { + ERC165Storage storage s = getStorage(); + s.supportedInterfaces[_interfaceId] = true; + } + + /// @notice Check if a contract supports an interface + /// @param _interfaceId The interface ID to check + /// @return True if the interface is supported, false otherwise + function supportsInterface(bytes4 _interfaceId) internal view returns (bool) { + ERC165Storage storage s = getStorage(); + // If the ERC165 interface itself is being queried, return true + // since this library implements ERC165 + if (_interfaceId == type(IERC165).interfaceId) { + return true; + } + return s.supportedInterfaces[_interfaceId]; + } +} From 0a11516c75f5ef446fabf71bd1c9ab731f577d80 Mon Sep 17 00:00:00 2001 From: ogazboiz Date: Mon, 27 Oct 2025 16:43:12 +0100 Subject: [PATCH 3/4] Refactor ERC165Facet to standalone architecture in interfaceDetection directory --- src/diamond/ERC165Facet.sol | 19 ------ src/interfaceDetection/ERC165/ERC165Facet.sol | 67 +++++++++++++++++++ src/interfaces/IERC165.sol | 15 ----- src/libraries/LibERC165.sol | 52 -------------- 4 files changed, 67 insertions(+), 86 deletions(-) delete mode 100644 src/diamond/ERC165Facet.sol create mode 100644 src/interfaceDetection/ERC165/ERC165Facet.sol delete mode 100644 src/interfaces/IERC165.sol delete mode 100644 src/libraries/LibERC165.sol diff --git a/src/diamond/ERC165Facet.sol b/src/diamond/ERC165Facet.sol deleted file mode 100644 index 0ca5e088..00000000 --- a/src/diamond/ERC165Facet.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.30; - -import {IERC165} from "../interfaces/IERC165.sol"; -import {LibERC165} from "../libraries/LibERC165.sol"; - -/// @title ERC165Facet — ERC-165 Standard Interface Detection Facet -/// @notice Facet implementation of ERC-165 for diamond proxy pattern -/// @dev Allows querying which interfaces are implemented by the diamond -contract ERC165Facet is IERC165 { - /// @notice Query if a contract implements an interface - /// @param _interfaceId The interface identifier, as specified in ERC-165 - /// @dev This function checks if the diamond supports the given interface ID - /// @return `true` if the contract implements `_interfaceId` and - /// `_interfaceId` is not 0xffffffff, `false` otherwise - function supportsInterface(bytes4 _interfaceId) external view override returns (bool) { - return LibERC165.supportsInterface(_interfaceId); - } -} diff --git a/src/interfaceDetection/ERC165/ERC165Facet.sol b/src/interfaceDetection/ERC165/ERC165Facet.sol new file mode 100644 index 00000000..bd9666cd --- /dev/null +++ b/src/interfaceDetection/ERC165/ERC165Facet.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.30; + +/// @title ERC-165 Standard Interface Detection Interface +/// @notice Interface for detecting what interfaces a contract implements +/// @dev ERC-165 allows contracts to publish their supported interfaces +interface IERC165 { + /// @notice Query if a contract implements an interface + /// @param _interfaceId The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `_interfaceId` and + /// `_interfaceId` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 _interfaceId) external view returns (bool); +} + +/// @title ERC165Facet — ERC-165 Standard Interface Detection Facet +/// @notice Facet implementation of ERC-165 for diamond proxy pattern +/// @dev Allows querying which interfaces are implemented by the diamond +/// Each facet is a standalone source code file following SCOP principles. +contract ERC165Facet { + /// @notice Storage slot identifier for ERC-165 interface detection + /// @dev Defined using keccak256 hash following ERC-8042 standard + bytes32 constant STORAGE_POSITION = keccak256("compose.erc165"); + + /// @notice ERC-165 storage layout using the ERC-8042 standard + /// @custom:storage-location erc8042:compose.erc165 + struct ERC165Storage { + /// @notice Mapping of interface IDs to whether they are supported + mapping(bytes4 => bool) supportedInterfaces; + } + + /// @notice Returns a pointer to the ERC-165 storage struct + /// @dev Uses inline assembly to bind the storage struct to the fixed storage position + /// @return s The ERC-165 storage struct + function getStorage() internal pure returns (ERC165Storage storage s) { + bytes32 position = STORAGE_POSITION; + assembly { + s.slot := position + } + } + + /// @notice Register that a contract supports an interface + /// @param _interfaceId The interface ID to register + /// @dev Call this function during initialization to register supported interfaces + function registerInterface(bytes4 _interfaceId) internal { + ERC165Storage storage s = getStorage(); + s.supportedInterfaces[_interfaceId] = true; + } + + /// @notice Query if a contract implements an interface + /// @param _interfaceId The interface identifier, as specified in ERC-165 + /// @dev This function checks if the diamond supports the given interface ID + /// @return `true` if the contract implements `_interfaceId` and + /// `_interfaceId` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 _interfaceId) external view returns (bool) { + ERC165Storage storage s = getStorage(); + + // If the ERC165 interface itself is being queried, return true + // since this facet implements ERC165 + if (_interfaceId == type(IERC165).interfaceId) { + return true; + } + + return s.supportedInterfaces[_interfaceId]; + } +} diff --git a/src/interfaces/IERC165.sol b/src/interfaces/IERC165.sol deleted file mode 100644 index 6ed2e484..00000000 --- a/src/interfaces/IERC165.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.30; - -/// @title ERC-165 Standard Interface Detection Interface -/// @notice Interface for detecting what interfaces a contract implements -/// @dev ERC-165 allows contracts to publish their supported interfaces -interface IERC165 { - /// @notice Query if a contract implements an interface - /// @param _interfaceId The interface identifier, as specified in ERC-165 - /// @dev Interface identification is specified in ERC-165. This function - /// uses less than 30,000 gas. - /// @return `true` if the contract implements `_interfaceId` and - /// `_interfaceId` is not 0xffffffff, `false` otherwise - function supportsInterface(bytes4 _interfaceId) external view returns (bool); -} diff --git a/src/libraries/LibERC165.sol b/src/libraries/LibERC165.sol deleted file mode 100644 index 897427ad..00000000 --- a/src/libraries/LibERC165.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.30; - -import {IERC165} from "../interfaces/IERC165.sol"; - -/// @title LibERC165 — ERC-165 Standard Interface Detection Library -/// @notice Provides internal functions and storage layout for ERC-165 interface detection. -/// @dev Uses ERC-8042 for storage location standardization -library LibERC165 { - /// @notice Storage slot identifier, defined using keccak256 hash of the library diamond storage identifier. - bytes32 constant STORAGE_POSITION = keccak256("compose.erc165"); - - /// @notice ERC-165 storage layout using the ERC-8042 standard. - /// @custom:storage-location erc8042:compose.erc165 - struct ERC165Storage { - /// @notice Mapping of interface IDs to whether they are supported - mapping(bytes4 => bool) supportedInterfaces; - } - - /// @notice Returns a pointer to the ERC-165 storage struct. - /// @dev Uses inline assembly to bind the storage struct to the fixed storage position. - /// @return s The ERC-165 storage struct. - function getStorage() internal pure returns (ERC165Storage storage s) { - bytes32 position = STORAGE_POSITION; - assembly { - s.slot := position - } - } - - /// @notice Register that a contract supports an interface - /// @param _interfaceId The interface ID to register - /// @dev Call this function during initialization to register supported interfaces. - /// For example, in an ERC721 facet initialization, you would call: - /// `LibERC165.registerInterface(type(IERC721).interfaceId)` - function registerInterface(bytes4 _interfaceId) internal { - ERC165Storage storage s = getStorage(); - s.supportedInterfaces[_interfaceId] = true; - } - - /// @notice Check if a contract supports an interface - /// @param _interfaceId The interface ID to check - /// @return True if the interface is supported, false otherwise - function supportsInterface(bytes4 _interfaceId) internal view returns (bool) { - ERC165Storage storage s = getStorage(); - // If the ERC165 interface itself is being queried, return true - // since this library implements ERC165 - if (_interfaceId == type(IERC165).interfaceId) { - return true; - } - return s.supportedInterfaces[_interfaceId]; - } -} From c7c53a0138fbf6f4cc706d51e2953f25d0606069 Mon Sep 17 00:00:00 2001 From: ogazboiz Date: Tue, 28 Oct 2025 00:35:15 +0100 Subject: [PATCH 4/4] Remove registerInterface from ERC165Facet and add LibERC165 library --- src/interfaceDetection/ERC165/ERC165Facet.sol | 8 ---- src/interfaceDetection/ERC165/LibERC165.sol | 37 +++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 src/interfaceDetection/ERC165/LibERC165.sol diff --git a/src/interfaceDetection/ERC165/ERC165Facet.sol b/src/interfaceDetection/ERC165/ERC165Facet.sol index bd9666cd..74e9025e 100644 --- a/src/interfaceDetection/ERC165/ERC165Facet.sol +++ b/src/interfaceDetection/ERC165/ERC165Facet.sol @@ -40,14 +40,6 @@ contract ERC165Facet { } } - /// @notice Register that a contract supports an interface - /// @param _interfaceId The interface ID to register - /// @dev Call this function during initialization to register supported interfaces - function registerInterface(bytes4 _interfaceId) internal { - ERC165Storage storage s = getStorage(); - s.supportedInterfaces[_interfaceId] = true; - } - /// @notice Query if a contract implements an interface /// @param _interfaceId The interface identifier, as specified in ERC-165 /// @dev This function checks if the diamond supports the given interface ID diff --git a/src/interfaceDetection/ERC165/LibERC165.sol b/src/interfaceDetection/ERC165/LibERC165.sol new file mode 100644 index 00000000..54fb1ced --- /dev/null +++ b/src/interfaceDetection/ERC165/LibERC165.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.30; + +/// @title LibERC165 — ERC-165 Standard Interface Detection Library +/// @notice Provides internal functions and storage layout for ERC-165 interface detection. +/// @dev Uses ERC-8042 for storage location standardization +library LibERC165 { + /// @notice Storage slot identifier, defined using keccak256 hash of the library diamond storage identifier. + bytes32 constant STORAGE_POSITION = keccak256("compose.erc165"); + + /// @notice ERC-165 storage layout using the ERC-8042 standard. + /// @custom:storage-location erc8042:compose.erc165 + struct ERC165Storage { + /// @notice Mapping of interface IDs to whether they are supported + mapping(bytes4 => bool) supportedInterfaces; + } + + /// @notice Returns a pointer to the ERC-165 storage struct. + /// @dev Uses inline assembly to bind the storage struct to the fixed storage position. + /// @return s The ERC-165 storage struct. + function getStorage() internal pure returns (ERC165Storage storage s) { + bytes32 position = STORAGE_POSITION; + assembly { + s.slot := position + } + } + + /// @notice Register that a contract supports an interface + /// @param _interfaceId The interface ID to register + /// @dev Call this function during initialization to register supported interfaces. + /// For example, in an ERC721 facet initialization, you would call: + /// `LibERC165.registerInterface(type(IERC721).interfaceId)` + function registerInterface(bytes4 _interfaceId) internal { + ERC165Storage storage s = getStorage(); + s.supportedInterfaces[_interfaceId] = true; + } +}