From 628f24d71d794f816a3333f6ca5b337aaa88ba79 Mon Sep 17 00:00:00 2001 From: YamenMerhi Date: Mon, 4 Sep 2023 17:50:58 +0300 Subject: [PATCH] feat: add LSP17Extendable to LSP7 and LSP8 --- .../LSP17ContractExtension/LSP17Errors.sol | 5 + .../LSP7DigitalAsset/LSP7DigitalAsset.sol | 124 ++++++++++++++++- .../LSP7DigitalAssetInitAbstract.sol | 125 +++++++++++++++++- .../LSP8IdentifiableDigitalAsset.sol | 125 +++++++++++++++++- ...P8IdentifiableDigitalAssetInitAbstract.sol | 125 +++++++++++++++++- 5 files changed, 492 insertions(+), 12 deletions(-) diff --git a/contracts/LSP17ContractExtension/LSP17Errors.sol b/contracts/LSP17ContractExtension/LSP17Errors.sol index 5eea988a9..d110e5aaf 100644 --- a/contracts/LSP17ContractExtension/LSP17Errors.sol +++ b/contracts/LSP17ContractExtension/LSP17Errors.sol @@ -5,3 +5,8 @@ pragma solidity ^0.8.4; * @dev reverts when there is no extension for the function selector being called with */ error NoExtensionFoundForFunctionSelector(bytes4 functionSelector); + +/** + * @dev reverts when the contract is called with a function selector not valid (less than 4 bytes of data) + */ +error InvalidFunctionSelector(bytes data); diff --git a/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol b/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol index abf0ce915..7906f055b 100644 --- a/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol +++ b/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol @@ -10,10 +10,23 @@ import { LSP4DigitalAssetMetadata } from "../LSP4DigitalAssetMetadata/LSP4DigitalAssetMetadata.sol"; import {LSP7DigitalAssetCore} from "./LSP7DigitalAssetCore.sol"; +import {LSP17Extendable} from "../LSP17ContractExtension/LSP17Extendable.sol"; + +// libraries +import {LSP2Utils} from "../LSP2ERC725YJSONSchema/LSP2Utils.sol"; // constants import {_INTERFACEID_LSP7} from "./LSP7Constants.sol"; +import "../LSP17ContractExtension/LSP17Constants.sol"; + +// errors + +import { + NoExtensionFoundForFunctionSelector, + InvalidFunctionSelector +} from "../LSP17ContractExtension/LSP17Errors.sol"; + /** * @title Implementation of a LSP7 Digital Asset, a contract that represents a fungible token. * @author Matthew Stevens @@ -25,7 +38,8 @@ import {_INTERFACEID_LSP7} from "./LSP7Constants.sol"; */ abstract contract LSP7DigitalAsset is LSP4DigitalAssetMetadata, - LSP7DigitalAssetCore + LSP7DigitalAssetCore, + LSP17Extendable { /** * @notice Sets the token-Metadata @@ -43,14 +57,118 @@ abstract contract LSP7DigitalAsset is _isNonDivisible = isNonDivisible_; } + // fallback function + + /** + * @notice The `fallback` function was called with the following amount of native tokens: `msg.value`; and the following calldata: `callData`. + * + * @dev Achieves the goal of [LSP-17-ContractExtension] standard by extending the contract to handle calls of functions that do not exist natively, + * forwarding the function call to the extension address mapped to the function being called. + * + * This function is executed when: + * - Sending data of length less than 4 bytes to the contract. + * - The first 4 bytes of the calldata do not match any publicly callable functions from the contract ABI. + * - Receiving native tokens + * + * 1. If the data is equal or longer than 4 bytes, the [ERC-725Y] storage is queried with the following data key: [_LSP17_EXTENSION_PREFIX] + `bytes4(msg.sig)` (Check [LSP-2-ERC725YJSONSchema] for encoding the data key) + * + * - If there is no address stored under the following data key, revert with {NoExtensionFoundForFunctionSelector(bytes4)}. The data key relative to `bytes4(0)` is an exception, where no reverts occurs if there is no extension address stored under. This exception is made to allow users to send random data (graffiti) to the account and to be able to react on it. + * + * - If there is an address, forward the `msg.data` to the extension using the CALL opcode, appending 52 bytes (20 bytes of `msg.sender` and 32 bytes of `msg.value`). Return what the calls returns, or revert if the call failed. + * + * 2. If the data sent to this function is of length less than 4 bytes (not a function selector), revert. + */ + fallback( + bytes calldata callData + ) external payable virtual returns (bytes memory) { + if (msg.data.length < 4) { + revert InvalidFunctionSelector(callData); + } + return _fallbackLSP17Extendable(callData); + } + + /** + * @dev Forwards the call with the received value to an extension mapped to a function selector. + * + * Calls {_getExtension} to get the address of the extension mapped to the function selector being + * called on the account. If there is no extension, the address(0) will be returned. + * + * Reverts if there is no extension for the function being called. + * + * If there is an extension for the function selector being called, it calls the extension with the + * CALL opcode, passing the {msg.data} appended with the 20 bytes of the {msg.sender} and + * 32 bytes of the {msg.value} + * + * Because the function uses assembly {return()/revert()} to terminate the call, it cannot be + * called before other codes in fallback(). + * + * Otherwise, the codes after _fallbackLSP17Extendable() may never be reached. + */ + function _fallbackLSP17Extendable( + bytes calldata callData + ) internal virtual override returns (bytes memory) { + // If there is a function selector + address extension = _getExtension(msg.sig); + + // if no extension was found, revert + if (extension == address(0)) + revert NoExtensionFoundForFunctionSelector(msg.sig); + + (bool success, bytes memory result) = extension.call{value: msg.value}( + abi.encodePacked(callData, msg.sender, msg.value) + ); + + if (success) { + return result; + } else { + // `mload(result)` -> offset in memory where `result.length` is located + // `add(result, 32)` -> offset in memory where `result` data starts + // solhint-disable no-inline-assembly + /// @solidity memory-safe-assembly + assembly { + let resultdata_size := mload(result) + revert(add(result, 32), resultdata_size) + } + } + } + + /** + * @dev Returns the extension address stored under the following data key: + * - {_LSP17_EXTENSION_PREFIX} + `` (Check [LSP2-ERC725YJSONSchema] for encoding the data key). + * - If no extension is stored, returns the address(0). + */ + function _getExtension( + bytes4 functionSelector + ) internal view virtual override returns (address) { + // Generate the data key relevant for the functionSelector being called + bytes32 mappedExtensionDataKey = LSP2Utils.generateMappingKey( + _LSP17_EXTENSION_PREFIX, + functionSelector + ); + + // Check if there is an extension stored under the generated data key + address extension = address( + bytes20(ERC725YCore._getData(mappedExtensionDataKey)) + ); + + return extension; + } + /** * @inheritdoc IERC165 */ function supportsInterface( bytes4 interfaceId - ) public view virtual override(IERC165, ERC725YCore) returns (bool) { + ) + public + view + virtual + override(IERC165, ERC725YCore, LSP17Extendable) + returns (bool) + { return interfaceId == _INTERFACEID_LSP7 || - super.supportsInterface(interfaceId); + super.supportsInterface(interfaceId) || + LSP17Extendable._supportsInterfaceInERC165Extension(interfaceId); } } diff --git a/contracts/LSP7DigitalAsset/LSP7DigitalAssetInitAbstract.sol b/contracts/LSP7DigitalAsset/LSP7DigitalAssetInitAbstract.sol index 9cb9a5b94..e3acdb39a 100644 --- a/contracts/LSP7DigitalAsset/LSP7DigitalAssetInitAbstract.sol +++ b/contracts/LSP7DigitalAsset/LSP7DigitalAssetInitAbstract.sol @@ -11,9 +11,23 @@ import { } from "../LSP4DigitalAssetMetadata/LSP4DigitalAssetMetadataInitAbstract.sol"; import {LSP7DigitalAssetCore} from "./LSP7DigitalAssetCore.sol"; +import {LSP17Extendable} from "../LSP17ContractExtension/LSP17Extendable.sol"; + +// libraries +import {LSP2Utils} from "../LSP2ERC725YJSONSchema/LSP2Utils.sol"; + // constants import {_INTERFACEID_LSP7} from "./LSP7Constants.sol"; +import "../LSP17ContractExtension/LSP17Constants.sol"; + +// errors + +import { + NoExtensionFoundForFunctionSelector, + InvalidFunctionSelector +} from "../LSP17ContractExtension/LSP17Errors.sol"; + /** * @title LSP7DigitalAsset contract * @author Matthew Stevens @@ -21,7 +35,8 @@ import {_INTERFACEID_LSP7} from "./LSP7Constants.sol"; */ abstract contract LSP7DigitalAssetInitAbstract is LSP4DigitalAssetMetadataInitAbstract, - LSP7DigitalAssetCore + LSP7DigitalAssetCore, + LSP17Extendable { function _initialize( string memory name_, @@ -37,14 +52,118 @@ abstract contract LSP7DigitalAssetInitAbstract is ); } + // fallback function + + /** + * @notice The `fallback` function was called with the following amount of native tokens: `msg.value`; and the following calldata: `callData`. + * + * @dev Achieves the goal of [LSP-17-ContractExtension] standard by extending the contract to handle calls of functions that do not exist natively, + * forwarding the function call to the extension address mapped to the function being called. + * + * This function is executed when: + * - Sending data of length less than 4 bytes to the contract. + * - The first 4 bytes of the calldata do not match any publicly callable functions from the contract ABI. + * - Receiving native tokens + * + * 1. If the data is equal or longer than 4 bytes, the [ERC-725Y] storage is queried with the following data key: [_LSP17_EXTENSION_PREFIX] + `bytes4(msg.sig)` (Check [LSP-2-ERC725YJSONSchema] for encoding the data key) + * + * - If there is no address stored under the following data key, revert with {NoExtensionFoundForFunctionSelector(bytes4)}. The data key relative to `bytes4(0)` is an exception, where no reverts occurs if there is no extension address stored under. This exception is made to allow users to send random data (graffiti) to the account and to be able to react on it. + * + * - If there is an address, forward the `msg.data` to the extension using the CALL opcode, appending 52 bytes (20 bytes of `msg.sender` and 32 bytes of `msg.value`). Return what the calls returns, or revert if the call failed. + * + * 2. If the data sent to this function is of length less than 4 bytes (not a function selector), revert. + */ + fallback( + bytes calldata callData + ) external payable virtual returns (bytes memory) { + if (msg.data.length < 4) { + revert InvalidFunctionSelector(callData); + } + return _fallbackLSP17Extendable(callData); + } + + /** + * @dev Forwards the call with the received value to an extension mapped to a function selector. + * + * Calls {_getExtension} to get the address of the extension mapped to the function selector being + * called on the account. If there is no extension, the address(0) will be returned. + * + * Reverts if there is no extension for the function being called. + * + * If there is an extension for the function selector being called, it calls the extension with the + * CALL opcode, passing the {msg.data} appended with the 20 bytes of the {msg.sender} and + * 32 bytes of the {msg.value} + * + * Because the function uses assembly {return()/revert()} to terminate the call, it cannot be + * called before other codes in fallback(). + * + * Otherwise, the codes after _fallbackLSP17Extendable() may never be reached. + */ + function _fallbackLSP17Extendable( + bytes calldata callData + ) internal virtual override returns (bytes memory) { + // If there is a function selector + address extension = _getExtension(msg.sig); + + // if no extension was found, revert + if (extension == address(0)) + revert NoExtensionFoundForFunctionSelector(msg.sig); + + (bool success, bytes memory result) = extension.call{value: msg.value}( + abi.encodePacked(callData, msg.sender, msg.value) + ); + + if (success) { + return result; + } else { + // `mload(result)` -> offset in memory where `result.length` is located + // `add(result, 32)` -> offset in memory where `result` data starts + // solhint-disable no-inline-assembly + /// @solidity memory-safe-assembly + assembly { + let resultdata_size := mload(result) + revert(add(result, 32), resultdata_size) + } + } + } + + /** + * @dev Returns the extension address stored under the following data key: + * - {_LSP17_EXTENSION_PREFIX} + `` (Check [LSP2-ERC725YJSONSchema] for encoding the data key). + * - If no extension is stored, returns the address(0). + */ + function _getExtension( + bytes4 functionSelector + ) internal view virtual override returns (address) { + // Generate the data key relevant for the functionSelector being called + bytes32 mappedExtensionDataKey = LSP2Utils.generateMappingKey( + _LSP17_EXTENSION_PREFIX, + functionSelector + ); + + // Check if there is an extension stored under the generated data key + address extension = address( + bytes20(ERC725YCore._getData(mappedExtensionDataKey)) + ); + + return extension; + } + /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface( bytes4 interfaceId - ) public view virtual override(IERC165, ERC725YCore) returns (bool) { + ) + public + view + virtual + override(IERC165, ERC725YCore, LSP17Extendable) + returns (bool) + { return interfaceId == _INTERFACEID_LSP7 || - super.supportsInterface(interfaceId); + super.supportsInterface(interfaceId) || + LSP17Extendable._supportsInterfaceInERC165Extension(interfaceId); } } diff --git a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol index 5942c8d6b..4fe219141 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol @@ -13,9 +13,23 @@ import { LSP4DigitalAssetMetadata } from "../LSP4DigitalAssetMetadata/LSP4DigitalAssetMetadata.sol"; +import {LSP17Extendable} from "../LSP17ContractExtension/LSP17Extendable.sol"; + +// libraries +import {LSP2Utils} from "../LSP2ERC725YJSONSchema/LSP2Utils.sol"; + // constants import {_INTERFACEID_LSP8} from "./LSP8Constants.sol"; +import "../LSP17ContractExtension/LSP17Constants.sol"; + +// errors + +import { + NoExtensionFoundForFunctionSelector, + InvalidFunctionSelector +} from "../LSP17ContractExtension/LSP17Errors.sol"; + /** * @title Implementation of a LSP8 Identifiable Digital Asset, a contract that represents a non-fungible token. * @author Matthew Stevens @@ -29,7 +43,8 @@ import {_INTERFACEID_LSP8} from "./LSP8Constants.sol"; */ abstract contract LSP8IdentifiableDigitalAsset is LSP4DigitalAssetMetadata, - LSP8IdentifiableDigitalAssetCore + LSP8IdentifiableDigitalAssetCore, + LSP17Extendable { /** * @notice Sets the token-Metadata @@ -43,14 +58,118 @@ abstract contract LSP8IdentifiableDigitalAsset is address newOwner_ ) LSP4DigitalAssetMetadata(name_, symbol_, newOwner_) {} + // fallback function + + /** + * @notice The `fallback` function was called with the following amount of native tokens: `msg.value`; and the following calldata: `callData`. + * + * @dev Achieves the goal of [LSP-17-ContractExtension] standard by extending the contract to handle calls of functions that do not exist natively, + * forwarding the function call to the extension address mapped to the function being called. + * + * This function is executed when: + * - Sending data of length less than 4 bytes to the contract. + * - The first 4 bytes of the calldata do not match any publicly callable functions from the contract ABI. + * - Receiving native tokens + * + * 1. If the data is equal or longer than 4 bytes, the [ERC-725Y] storage is queried with the following data key: [_LSP17_EXTENSION_PREFIX] + `bytes4(msg.sig)` (Check [LSP-2-ERC725YJSONSchema] for encoding the data key) + * + * - If there is no address stored under the following data key, revert with {NoExtensionFoundForFunctionSelector(bytes4)}. The data key relative to `bytes4(0)` is an exception, where no reverts occurs if there is no extension address stored under. This exception is made to allow users to send random data (graffiti) to the account and to be able to react on it. + * + * - If there is an address, forward the `msg.data` to the extension using the CALL opcode, appending 52 bytes (20 bytes of `msg.sender` and 32 bytes of `msg.value`). Return what the calls returns, or revert if the call failed. + * + * 2. If the data sent to this function is of length less than 4 bytes (not a function selector), revert. + */ + fallback( + bytes calldata callData + ) external payable virtual returns (bytes memory) { + if (msg.data.length < 4) { + revert InvalidFunctionSelector(callData); + } + return _fallbackLSP17Extendable(callData); + } + + /** + * @dev Forwards the call with the received value to an extension mapped to a function selector. + * + * Calls {_getExtension} to get the address of the extension mapped to the function selector being + * called on the account. If there is no extension, the address(0) will be returned. + * + * Reverts if there is no extension for the function being called. + * + * If there is an extension for the function selector being called, it calls the extension with the + * CALL opcode, passing the {msg.data} appended with the 20 bytes of the {msg.sender} and + * 32 bytes of the {msg.value} + * + * Because the function uses assembly {return()/revert()} to terminate the call, it cannot be + * called before other codes in fallback(). + * + * Otherwise, the codes after _fallbackLSP17Extendable() may never be reached. + */ + function _fallbackLSP17Extendable( + bytes calldata callData + ) internal virtual override returns (bytes memory) { + // If there is a function selector + address extension = _getExtension(msg.sig); + + // if no extension was found, revert + if (extension == address(0)) + revert NoExtensionFoundForFunctionSelector(msg.sig); + + (bool success, bytes memory result) = extension.call{value: msg.value}( + abi.encodePacked(callData, msg.sender, msg.value) + ); + + if (success) { + return result; + } else { + // `mload(result)` -> offset in memory where `result.length` is located + // `add(result, 32)` -> offset in memory where `result` data starts + // solhint-disable no-inline-assembly + /// @solidity memory-safe-assembly + assembly { + let resultdata_size := mload(result) + revert(add(result, 32), resultdata_size) + } + } + } + + /** + * @dev Returns the extension address stored under the following data key: + * - {_LSP17_EXTENSION_PREFIX} + `` (Check [LSP2-ERC725YJSONSchema] for encoding the data key). + * - If no extension is stored, returns the address(0). + */ + function _getExtension( + bytes4 functionSelector + ) internal view virtual override returns (address) { + // Generate the data key relevant for the functionSelector being called + bytes32 mappedExtensionDataKey = LSP2Utils.generateMappingKey( + _LSP17_EXTENSION_PREFIX, + functionSelector + ); + + // Check if there is an extension stored under the generated data key + address extension = address( + bytes20(ERC725YCore._getData(mappedExtensionDataKey)) + ); + + return extension; + } + /** * @inheritdoc IERC165 */ function supportsInterface( bytes4 interfaceId - ) public view virtual override(IERC165, ERC725YCore) returns (bool) { + ) + public + view + virtual + override(IERC165, ERC725YCore, LSP17Extendable) + returns (bool) + { return interfaceId == _INTERFACEID_LSP8 || - super.supportsInterface(interfaceId); + super.supportsInterface(interfaceId) || + LSP17Extendable._supportsInterfaceInERC165Extension(interfaceId); } } diff --git a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetInitAbstract.sol b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetInitAbstract.sol index 14af58cb4..7a9bf469c 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetInitAbstract.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetInitAbstract.sol @@ -13,9 +13,23 @@ import { LSP4DigitalAssetMetadataInitAbstract } from "../LSP4DigitalAssetMetadata/LSP4DigitalAssetMetadataInitAbstract.sol"; +import {LSP17Extendable} from "../LSP17ContractExtension/LSP17Extendable.sol"; + +// libraries +import {LSP2Utils} from "../LSP2ERC725YJSONSchema/LSP2Utils.sol"; + // constants import {_INTERFACEID_LSP8} from "./LSP8Constants.sol"; +import "../LSP17ContractExtension/LSP17Constants.sol"; + +// errors + +import { + NoExtensionFoundForFunctionSelector, + InvalidFunctionSelector +} from "../LSP17ContractExtension/LSP17Errors.sol"; + /** * @title Implementation of a LSP8 Identifiable Digital Asset, a contract that represents a non-fungible token. * @author Matthew Stevens @@ -29,7 +43,8 @@ import {_INTERFACEID_LSP8} from "./LSP8Constants.sol"; */ abstract contract LSP8IdentifiableDigitalAssetInitAbstract is LSP4DigitalAssetMetadataInitAbstract, - LSP8IdentifiableDigitalAssetCore + LSP8IdentifiableDigitalAssetCore, + LSP17Extendable { function _initialize( string memory name_, @@ -43,14 +58,118 @@ abstract contract LSP8IdentifiableDigitalAssetInitAbstract is ); } + // fallback function + + /** + * @notice The `fallback` function was called with the following amount of native tokens: `msg.value`; and the following calldata: `callData`. + * + * @dev Achieves the goal of [LSP-17-ContractExtension] standard by extending the contract to handle calls of functions that do not exist natively, + * forwarding the function call to the extension address mapped to the function being called. + * + * This function is executed when: + * - Sending data of length less than 4 bytes to the contract. + * - The first 4 bytes of the calldata do not match any publicly callable functions from the contract ABI. + * - Receiving native tokens + * + * 1. If the data is equal or longer than 4 bytes, the [ERC-725Y] storage is queried with the following data key: [_LSP17_EXTENSION_PREFIX] + `bytes4(msg.sig)` (Check [LSP-2-ERC725YJSONSchema] for encoding the data key) + * + * - If there is no address stored under the following data key, revert with {NoExtensionFoundForFunctionSelector(bytes4)}. The data key relative to `bytes4(0)` is an exception, where no reverts occurs if there is no extension address stored under. This exception is made to allow users to send random data (graffiti) to the account and to be able to react on it. + * + * - If there is an address, forward the `msg.data` to the extension using the CALL opcode, appending 52 bytes (20 bytes of `msg.sender` and 32 bytes of `msg.value`). Return what the calls returns, or revert if the call failed. + * + * 2. If the data sent to this function is of length less than 4 bytes (not a function selector), revert. + */ + fallback( + bytes calldata callData + ) external payable virtual returns (bytes memory) { + if (msg.data.length < 4) { + revert InvalidFunctionSelector(callData); + } + return _fallbackLSP17Extendable(callData); + } + + /** + * @dev Forwards the call with the received value to an extension mapped to a function selector. + * + * Calls {_getExtension} to get the address of the extension mapped to the function selector being + * called on the account. If there is no extension, the address(0) will be returned. + * + * Reverts if there is no extension for the function being called. + * + * If there is an extension for the function selector being called, it calls the extension with the + * CALL opcode, passing the {msg.data} appended with the 20 bytes of the {msg.sender} and + * 32 bytes of the {msg.value} + * + * Because the function uses assembly {return()/revert()} to terminate the call, it cannot be + * called before other codes in fallback(). + * + * Otherwise, the codes after _fallbackLSP17Extendable() may never be reached. + */ + function _fallbackLSP17Extendable( + bytes calldata callData + ) internal virtual override returns (bytes memory) { + // If there is a function selector + address extension = _getExtension(msg.sig); + + // if no extension was found, revert + if (extension == address(0)) + revert NoExtensionFoundForFunctionSelector(msg.sig); + + (bool success, bytes memory result) = extension.call{value: msg.value}( + abi.encodePacked(callData, msg.sender, msg.value) + ); + + if (success) { + return result; + } else { + // `mload(result)` -> offset in memory where `result.length` is located + // `add(result, 32)` -> offset in memory where `result` data starts + // solhint-disable no-inline-assembly + /// @solidity memory-safe-assembly + assembly { + let resultdata_size := mload(result) + revert(add(result, 32), resultdata_size) + } + } + } + + /** + * @dev Returns the extension address stored under the following data key: + * - {_LSP17_EXTENSION_PREFIX} + `` (Check [LSP2-ERC725YJSONSchema] for encoding the data key). + * - If no extension is stored, returns the address(0). + */ + function _getExtension( + bytes4 functionSelector + ) internal view virtual override returns (address) { + // Generate the data key relevant for the functionSelector being called + bytes32 mappedExtensionDataKey = LSP2Utils.generateMappingKey( + _LSP17_EXTENSION_PREFIX, + functionSelector + ); + + // Check if there is an extension stored under the generated data key + address extension = address( + bytes20(ERC725YCore._getData(mappedExtensionDataKey)) + ); + + return extension; + } + /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface( bytes4 interfaceId - ) public view virtual override(IERC165, ERC725YCore) returns (bool) { + ) + public + view + virtual + override(IERC165, ERC725YCore, LSP17Extendable) + returns (bool) + { return interfaceId == _INTERFACEID_LSP8 || - super.supportsInterface(interfaceId); + super.supportsInterface(interfaceId) || + LSP17Extendable._supportsInterfaceInERC165Extension(interfaceId); } }