diff --git a/src/abstracts/CloneDistribution.sol b/src/abstracts/CloneDistribution.sol index c6fe3b0..3e90394 100644 --- a/src/abstracts/CloneDistribution.sol +++ b/src/abstracts/CloneDistribution.sol @@ -10,6 +10,7 @@ abstract contract CloneDistribution is IDistribution, CodeIndexer { function sources() internal view virtual returns (address[] memory, bytes32 name, uint256 version); + // @inheritdoc IDistribution function instantiate( bytes memory ) external virtual returns (address[] memory instances, bytes32 distributionName, uint256 distributionVersion) { @@ -23,10 +24,10 @@ abstract contract CloneDistribution is IDistribution, CodeIndexer { emit Distributed(msg.sender, instances); return (instances, _distributionName, _distributionVersion); } - + // @inheritdoc IDistribution function get() external view virtual returns (address[] memory src, bytes32 name, uint256 version) { return sources(); } - + // @inheritdoc IDistribution function getMetadata() external view virtual returns (string memory); } diff --git a/src/abstracts/CodeIndexer.sol b/src/abstracts/CodeIndexer.sol index 0d5e47b..018fce4 100644 --- a/src/abstracts/CodeIndexer.sol +++ b/src/abstracts/CodeIndexer.sol @@ -7,11 +7,11 @@ abstract contract CodeIndexer { //Create2 contract ICodeIndex private constant INDEX_CONTRACT = ICodeIndex(0xc0D31d398c5ee86C5f8a23FA253ee8a586dA03Ce); constructor() {} - + // @inheritdoc ICodeIndex function getContractsIndex() internal pure returns (ICodeIndex) { return INDEX_CONTRACT; } - + // @inheritdoc ICodeIndex function index(address source) internal { INDEX_CONTRACT.register(source); } diff --git a/src/abstracts/Distributor.sol b/src/abstracts/Distributor.sol index fb8115f..d269907 100644 --- a/src/abstracts/Distributor.sol +++ b/src/abstracts/Distributor.sol @@ -7,6 +7,13 @@ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "../interfaces/IInitializer.sol"; import "../abstracts/CodeIndexer.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +/** + * @title Distributor + * @notice Abstract contract that implements the IDistributor interface, CodeIndexer, and ERC165. + * This contract serves as a base for creating distributor contracts with specific functionalities. + * It provides the necessary structure and functions to be extended by other contracts. + * @author Peeramid Labs, 2024 + */ abstract contract Distributor is IDistributor, CodeIndexer, ERC165 { struct DistributionComponent { bytes32 id; @@ -19,19 +26,19 @@ abstract contract Distributor is IDistributor, CodeIndexer, ERC165 { mapping(uint256 => bytes32) public distributionOf; mapping(bytes32 => DistributionComponent) public distributionComponents; uint256 public numInstances; - + // @inheritdoc IDistributor function getDistributions() external view returns (bytes32[] memory) { return distirbutionsSet.values(); } - + // @inheritdoc IDistributor function getDistributionId(address instance) external view virtual returns (bytes32) { return distributionOf[getInstanceId(instance)]; } - + // @inheritdoc IDistributor function getInstanceId(address instance) public view virtual returns (uint256) { return instanceIds[instance]; } - + // @inheritdoc IDistributor function getDistributionURI(bytes32 distributorsId) external view returns (string memory) { DistributionComponent memory distributionComponent = distributionComponents[distributorsId]; ICodeIndex codeIndex = getContractsIndex(); @@ -55,6 +62,10 @@ abstract contract Distributor is IDistributor, CodeIndexer, ERC165 { emit DistributionRemoved(distributorsId); } + /** + * @notice Internal function to instantiate a new instance. + * @dev This function will DELEGATECALL to initializer. Initializer MUST be trusted contract. + */ function _instantiate( bytes32 distributorsId, bytes memory args @@ -92,13 +103,10 @@ abstract contract Distributor is IDistributor, CodeIndexer, ERC165 { return (instances, distributionName, distributionVersion); } - /* - * @dev This is ERC7746 implementation - * This hook must be called by instance methods that access scope is - * limited to the same instance or distribution - * it will revert if `msg.sender` is not a valid instance - * it will revert if `maybeInstance` is not a valid instance - * it will revert if instanceId belongs to disactivated distribution + /** + * @inheritdoc IERC7746 + * @notice This is ERC7746 hook must be called by instance methods that access scope is limited to the same instance or distribution + * @dev it will revert if: (1) `msg.sender` is not a valid instance; (2) `maybeInstance` is not a valid instance (3) `instanceId` belongs to disactivated distribution */ function beforeCall( bytes memory config, @@ -120,7 +128,11 @@ abstract contract Distributor is IDistributor, CodeIndexer, ERC165 { } revert InvalidInstance(maybeInstance); } - + /** + * @inheritdoc IERC7746 + * @notice This is ERC7746 hook must be called by instance methods that access scope is limited to the same instance or distribution + * @dev it will revert if: (1) `msg.sender` is not a valid instance; (2) `maybeInstance` is not a valid instance (3) `instanceId` belongs to disactivated distribution + */ function afterCall( bytes memory config, bytes4, diff --git a/src/abstracts/ERC7746Middleware.sol b/src/abstracts/ERC7746Middleware.sol index fad1a40..e9cf12e 100644 --- a/src/abstracts/ERC7746Middleware.sol +++ b/src/abstracts/ERC7746Middleware.sol @@ -3,13 +3,33 @@ pragma solidity >=0.8.0 <0.9.0; import "../libraries/LibMiddleware.sol"; +/** + * @title ERC7746Middleware + * @notice Abstract contract that serves as a middleware for ERC7746 standard. + * This contract is intended to be inherited by other contracts that implement + * the ERC7746 functionality. It provides base functionality and structure + * that can be extended and customized by derived contracts. + * @author Peeramid Labs, 2024 + */ abstract contract ERC7746Middleware { + /** + * @notice Modifier to apply custom logic for ERC7746 compliance. + * @param _selector The function selector to be checked. + * @param sender The address of the sender. + * @param data The calldata being passed to the function. + * @param value The value being transferred. + */ modifier ERC7746C(bytes4 _selector, address sender, bytes calldata data, uint256 value) { bytes[] memory layerReturns = LibMiddleware.beforeCall(_selector, sender, data, value); _; LibMiddleware.afterCall(_selector, sender, data, value, layerReturns); } + /** + * @notice Modifier to apply ERC7746 specific logic. + * This modifier can be used to enforce certain conditions or + * execute specific code before or after the function it modifies. + */ modifier ERC7746() { bytes[] memory layerReturns = LibMiddleware.beforeCall(msg.sig, msg.sender, msg.data, msg.value); _; diff --git a/src/abstracts/Installer.sol b/src/abstracts/Installer.sol index 029ec7d..3716c19 100644 --- a/src/abstracts/Installer.sol +++ b/src/abstracts/Installer.sol @@ -3,6 +3,12 @@ pragma solidity >=0.8.0 <0.9.0; import "../interfaces/IDistributor.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "../interfaces/IInstaller.sol"; +/** + * @title Installer + * @notice Abstract contract that implements the IInstaller interface. + * This contract serves as a base for other contracts that require installation functionality. + * @author Peeramid Labs, 2024 + */ abstract contract Installer is IInstaller { using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.Bytes32Set; diff --git a/src/abstracts/InstallerCloneable.sol b/src/abstracts/InstallerCloneable.sol index b261a41..17e0056 100644 --- a/src/abstracts/InstallerCloneable.sol +++ b/src/abstracts/InstallerCloneable.sol @@ -5,6 +5,11 @@ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../interfaces/IInstaller.sol"; import "../libraries/LibInstaller.sol"; +/** + * @title InstallerClonable + * @notice Abstract contract that implements the IInstaller interface and is Initializable. + * This contract serves as a base for creating clonable installer contracts. + */ abstract contract InstallerClonable is IInstaller, Initializable { using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.Bytes32Set; @@ -12,19 +17,19 @@ abstract contract InstallerClonable is IInstaller, Initializable { constructor() { _disableInitializers(); } - + // @inheritdoc IInstaller function initialize(address targetAddress) public virtual initializer { LibInstaller.getStorage()._target = targetAddress; } - + // @inheritdoc IInstaller function isDistributor(IDistributor distributor) public view returns (bool) { return LibInstaller.getStorage().whitelistedDistributors.contains(address(distributor)); } - + // @inheritdoc IInstaller function getWhitelistedDistributors() public view returns (address[] memory) { return LibInstaller.getStorage().whitelistedDistributors.values(); } - + // @inheritdoc IInstaller function whitelistedDistributions(IDistributor distributor) public view returns (bytes32[] memory) { if (LibInstaller.getStorage().whitelistedDistributors.contains(address(distributor))) { return distributor.getDistributions(); @@ -98,27 +103,29 @@ abstract contract InstallerClonable is IInstaller, Initializable { } strg.instancesNum--; } - + // @inheritdoc IInstaller function getInstance(uint256 instanceId) public view returns (address[] memory instaneContracts) { return LibInstaller.getStorage()._instanceEnum[instanceId]; } - + // @inheritdoc IInstaller function getInstancesNum() public view returns (uint256) { return LibInstaller.getStorage().instancesNum; } - + // @inheritdoc IInstaller function isInstance(address instance) public view returns (bool) { return LibInstaller.getStorage()._distributorOf[instance] != address(0); } - + // @inheritdoc IInstaller function distributorOf(address instance) public view returns (IDistributor) { return IDistributor(LibInstaller.getStorage()._distributorOf[instance]); } - + // @inheritdoc IInstaller function target() public view returns (address) { return LibInstaller.getStorage()._target; } - + // @inheritdoc IERC7746 + // @notice this will revert if the sender is not the target or requesting instance is not a valid instance + // @dev it will daisy-chain the call to the distributor, if instance is valid and active distribution function beforeCall( bytes memory layerConfig, bytes4 selector, @@ -145,7 +152,9 @@ abstract contract InstallerClonable is IInstaller, Initializable { } revert NotAnInstance(requestingInstance); } - + // @inheritdoc IERC7746 + // @notice this will revert if the sender is not the target or requesting instance is not a valid instance + // @dev it will daisy-chain the call to the distributor, if instance is valid and active distribution function afterCall( bytes memory layerConfig, bytes4 selector, diff --git a/src/abstracts/MiddlewareProxy.sol b/src/abstracts/MiddlewareProxy.sol index bc61f37..58a0d18 100644 --- a/src/abstracts/MiddlewareProxy.sol +++ b/src/abstracts/MiddlewareProxy.sol @@ -7,7 +7,7 @@ import "../libraries/LibMiddleware.sol"; /** * @dev This contract is a modified OpenZeppelin proxy v5.0.0. * Modification wraps a fallback function within ERC7746. - * Rest is similar to OpenZeppelin Proxy.sol + * Rest is similar to Proxy.sol */ contract MiddlewareProxy is ERC7746Middleware { address private immutable implementationAddress; @@ -47,8 +47,9 @@ contract MiddlewareProxy is ERC7746Middleware { } /** - * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other + * @notice Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * function in the contract matches the call data. + * @dev This function is wrapped in ERC7746 middleware pattern */ fallback() external payable virtual ERC7746 { _fallback(); diff --git a/src/abstracts/Repository.sol b/src/abstracts/Repository.sol index fa17ceb..d7c61cf 100644 --- a/src/abstracts/Repository.sol +++ b/src/abstracts/Repository.sol @@ -1,9 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.8; +pragma solidity >=0.8.0 <0.9.0; import "../libraries/LibSemver.sol"; import "../interfaces/IRepository.sol"; +/** + * @title Repository + * @notice Abstract contract that implements the IRepository interface. This contract serves as a base for other contracts that require repository functionalities. + */ abstract contract Repository is IRepository { bytes32 public immutable repositoryName; using LibSemver for LibSemver.Version; @@ -19,13 +23,6 @@ abstract contract Repository is IRepository { constructor(bytes32 _repositoryName) { repositoryName = _repositoryName; } - // error VersionHashDoesNotExist(uint256 version); - // error ReleaseZeroNotAllowed(); - // error AlreadyInPreviousRelease(uint256 version, bytes32 source); - // error EmptyReleaseMetadata(); - // error ReleaseDoesNotExist(); - // event VersionAdded(uint256 indexed version, bytes32 indexed source, bytes buildMetadata); - // event ReleaseMetadataUpdated(uint256 indexed version, bytes releaseMetadata); function _updateReleaseMetadata(LibSemver.Version memory version, bytes memory metadata) internal { uint256 versionFlat = version.toUint256(); @@ -72,6 +69,7 @@ abstract contract Repository is IRepository { latestVersion = versionFlat; emit VersionAdded(versionFlat, sourceId, metadata); } + // @inheritdoc IRepository function getLatest() public view returns (Source memory) { Source memory src; src.sourceId = versionedSources[latestVersion]; @@ -79,6 +77,7 @@ abstract contract Repository is IRepository { src.metadata = releaseMetadata[uint64(latestVersion)]; return src; } + // @inheritdoc IRepository function get( LibSemver.Version memory version, LibSemver.requirements requirement @@ -146,22 +145,27 @@ abstract contract Repository is IRepository { bytes memory patchMetadata = patchReleaseMetadata[versionFlat]; return bytes.concat(majorMetadata, minorMetadata, patchMetadata); } - + // @inheritdoc IRepository function getMajorReleaseMetadata(uint64 major) public view returns (bytes memory) { return releaseMetadata[major]; } + // @inheritdoc IRepository function getMinorReleaseMetadata(uint64 major, uint64 minor) public view returns (bytes memory) { return minorReleaseMetadata[(uint128(major) << 64) | uint128(minor)]; } + // @inheritdoc IRepository function getPatchReleaseMetadata(uint64 major, uint64 minor, uint64 patch) public view returns (bytes memory) { return patchReleaseMetadata[(uint256(major) << 192) | (uint256(minor) << 128) | uint256(patch)]; } + // @inheritdoc IRepository function getMajorReleases() public view returns (uint64) { return majorReleases; } + // @inheritdoc IRepository function getMinorReleases(uint64 major) public view returns (uint64) { return minorReleases[major]; } + // @inheritdoc IRepository function getPatchReleases(uint64 major, uint64 minor) public view returns (uint128) { return patchReleases[(uint128(major) << 64) | uint128(minor)]; } diff --git a/src/distributions/CodeHashDistribution.sol b/src/distributions/CodeHashDistribution.sol index c7364af..59e3555 100644 --- a/src/distributions/CodeHashDistribution.sol +++ b/src/distributions/CodeHashDistribution.sol @@ -3,12 +3,24 @@ pragma solidity >=0.8.0 <0.9.0; import "../abstracts/CloneDistribution.sol"; +/** + * @title CodeHashDistribution + * @notice This contract creates immutable. It allows to add metadata to the distribution as well as specify name and version for EIP712 compatability reasons. + * @dev This contract is a base for creating distributions that are identified by a deployed functionality (byte code hash). + */ contract CodeHashDistribution is CloneDistribution { bytes32 private immutable metadata; address private immutable _reference; bytes32 public immutable distributionName; uint256 public immutable distributionVersion; + /** + * @notice Constructor for the CodeHashDistribution contract. + * @param codeHash The hash of the code to be distributed. + * @param _metadata Metadata associated with the code. + * @param name The name of the distribution. + * @param version The version number of the distribution. + */ constructor(bytes32 codeHash, bytes32 _metadata, bytes32 name, uint256 version) { distributionName = name; distributionVersion = version; @@ -19,13 +31,13 @@ contract CodeHashDistribution is CloneDistribution { revert CodeNotFoundInIndex(codeHash); } } - + // @inheritdoc IDistribution function sources() internal view virtual override returns (address[] memory, bytes32 name, uint256 version) { address[] memory _sources = new address[](1); _sources[0] = _reference; return (_sources, distributionName, distributionVersion); } - + // @inheritdoc IDistribution function getMetadata() external view virtual override returns (string memory) { return string(abi.encodePacked(metadata)); //ToDo: Add IPFS link with readme! } diff --git a/src/distributions/LatestVersionDistribution.sol b/src/distributions/LatestVersionDistribution.sol index 56c8ba0..fdb5286 100644 --- a/src/distributions/LatestVersionDistribution.sol +++ b/src/distributions/LatestVersionDistribution.sol @@ -5,10 +5,21 @@ import "../abstracts/CloneDistribution.sol"; import "../interfaces/IRepository.sol"; import "../libraries/LibSemver.sol"; +/** + * @title LatestVersionDistribution + * @notice This contract extends the CloneDistribution contract to manage the distribution of the latest version of a given resource. + * It provides mechanisms to ensure that the most recent version is distributed to users. + * @dev it MUST refer to {../interfaces/IRepository} + */ contract LatestVersionDistribution is CloneDistribution { bytes32 private immutable metadata; IRepository public immutable repository; + /** + * @notice Constructor for the LatestVersionDistribution contract. + * @param _repository The address of the IRepository contract. + * @param _metadata The metadata associated with the distribution. + */ constructor(IRepository _repository, bytes32 _metadata) { metadata = _metadata; repository = _repository; diff --git a/src/distributions/SACMDistribution.SOL.sol b/src/distributions/SACMDistribution.SOL.sol deleted file mode 100644 index 971589f..0000000 --- a/src/distributions/SACMDistribution.SOL.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0 <0.9.0; - -import "../abstracts/CloneDistribution.sol"; - -contract SACMDistribution is CloneDistribution { - bytes32 private immutable metadata; - address private immutable _reference; - bytes32 public immutable distributionName; - uint256 public immutable distributionVersion; - - constructor(bytes32 codeHash, bytes32 _metadata, bytes32 name, uint256 version) { - distributionName = name; - distributionVersion = version; - metadata = _metadata; - ICodeIndex index = getContractsIndex(); - _reference = index.get(codeHash); - if (_reference == address(0)) { - revert CodeNotFoundInIndex(codeHash); - } - } - - function sources() internal view virtual override returns (address[] memory, bytes32 name, uint256 version) { - address[] memory _sources = new address[](1); - _sources[0] = _reference; - return (_sources, distributionName, distributionVersion); - } - - function getMetadata() external view virtual override returns (string memory) { - return string(abi.encodePacked(metadata)); //ToDo: Add IPFS link with readme! - } -} diff --git a/src/interfaces/IDistribution.sol b/src/interfaces/IDistribution.sol index d2a69a5..41d35b9 100644 --- a/src/interfaces/IDistribution.sol +++ b/src/interfaces/IDistribution.sol @@ -1,13 +1,38 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; +/** + * @title IDistribution + * @notice Interface for distribution-related functionalities. It can get sources and produce a new instances out from them. It also provides metadata about the distribution. + * @dev It is highly recommended to keep implementation stateless, and use `immutable` variables for any state. This allows your code to be referred in distributor and respositories via ERC7744. It's also easier to reason about, and more gas efficient. + * @author Peeramid Labs, 2024 + */ interface IDistribution { + /** + * @notice Emitted when a distribution occurs. + * @param distributor The address of the entity that performed the distribution. + * @param instances An array of addresses that were produced. + */ event Distributed(address indexed distributor, address[] instances); + /** + * @notice Instantiates a new instance with the given parameters. + * @param data The data to be used for instantiation. + * @return instances An array of addresses that were produced. + * @return distributionName The name of the distribution. + * @return distributionVersion The version of the distribution. + * @dev WARNING: It MUST emit Distributed event. + */ function instantiate( bytes memory data ) external returns (address[] memory instances, bytes32 distributionName, uint256 distributionVersion); + /** + * @notice Retrieves the current distribution sources. + * @return sources An array of addresses that are used for instantiation. + * @return distributionName The name of the distribution. + * @return distributionVersion The version of the distribution. + */ function get() external view diff --git a/src/interfaces/IDistributor.sol b/src/interfaces/IDistributor.sol index de95e95..ee187d2 100644 --- a/src/interfaces/IDistributor.sol +++ b/src/interfaces/IDistributor.sol @@ -2,6 +2,12 @@ pragma solidity >=0.8.0 <0.9.0; import {IERC7746} from "../interfaces/IERC7746.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +/** + * @title IDistributor Interface + * @notice Defines the standard functions for a distributor contract. + * @dev If you want to use {IRepository} for versioned distributions, use {IVersionDistributor} interface. + * @author Peeramid Labs, 2024 + */ interface IDistributor is IERC7746, IERC165 { error DistributionNotFound(bytes32 id); error DistributionExists(bytes32 id); diff --git a/src/interfaces/IERC7746.sol b/src/interfaces/IERC7746.sol index aa63c68..80bcedf 100644 --- a/src/interfaces/IERC7746.sol +++ b/src/interfaces/IERC7746.sol @@ -1,9 +1,13 @@ // SPDX-License-Identifier: CC0-1.0 pragma solidity >=0.8.0 <0.9.0; +/** + * @title IERC7746 Interface + * @dev Interface for the ERC7746 standard. + */ interface IERC7746 { /// @notice Validates a function call before execution. - /// @param configuration Layer-specific configuration data. + /// @param configuration Middleware-specific configuration data. /// @param selector The function selector being called. /// @param sender The address initiating the call. /// @param value The amount of ETH sent with the call (if any). @@ -19,7 +23,7 @@ interface IERC7746 { ) external returns (bytes memory); /// @notice Validates a function call after execution. - /// @param configuration Layer-specific configuration data. + /// @param configuration Middleware-specific configuration data. /// @param selector The function selector being called. /// @param sender The address initiating the call. /// @param value The amount of ETH sent with the call (if any). diff --git a/src/interfaces/IInitializer.sol b/src/interfaces/IInitializer.sol index f8caf55..8d9472d 100644 --- a/src/interfaces/IInitializer.sol +++ b/src/interfaces/IInitializer.sol @@ -1,10 +1,24 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; +/** + * @title IInitializer + * @notice Interface for the Initializer contract. This is intended to be used + * as distribution initializer within the Distributor contract. + */ interface IInitializer { event Initialized(address indexed container, bytes32 indexed codeHash); error initializationFailed(bytes32 id, string reason); + /** + * @notice Initializes the contract with necessary parameters. + * @dev This function should be delegete-called by the distributor contract. + * @param distributionId The ID of the distribution being initialized + * @param instances The addresses of the instances being initialized + * @param distributionName The name of the distribution + * @param distributionVersion The version of the distribution + * @param args The additional arguments to be used for initialization + */ function initialize( bytes32 distributionId, address[] memory instances, diff --git a/src/interfaces/IInstaller.sol b/src/interfaces/IInstaller.sol index aa8c8e1..9d792fe 100644 --- a/src/interfaces/IInstaller.sol +++ b/src/interfaces/IInstaller.sol @@ -4,53 +4,188 @@ pragma solidity >=0.8.0 <0.9.0; import {IDistributor} from "./IDistributor.sol"; import {IERC7746} from "../interfaces/IERC7746.sol"; +/** + * @title IInstaller Interface + * @notice Enables target smart account to interact with Distributor contract ecosystem. + */ interface IInstaller is IERC7746 { + /** + * @dev Error indicating that the provided address is not a valid instance. + * @param instance The address that was checked and found not to be an instance. + */ + error NotAnInstance(address instance); + + /** + * @notice Error indicating that the provided distributor is invalid. + * @param distributor The distributor that is considered invalid. + */ error InvalidDistributor(IDistributor distributor); + /** + * @dev Error indicating that the provided target address is not the smart account installer serves. + * @param target The address that is considered invalid. + */ error InvalidTarget(address target); + /** + * @notice Error indicating that the specified distributor is already allowed. + * @param distributor The distributor that is already allowed. + */ error alreadyAllowed(IDistributor distributor); + /** + * @notice Error indicating that a distribution is not permitted (not installed). + * @param distributor The address of the distributor containing the distribution. + * @param distributionId The unique identifier of the distribution. + */ error DistributionIsNotPermitted(IDistributor distributor, bytes32 distributionId); + /** + * @notice Error indicating that distributor is whitelisted and hence it is not possible to selectively dissalow distriributions. + * @param distributor The whitelisted distributor. + * @param distributionId The ID of the distribution that was attempted to dissalow. + * @dev If getting this error: consider first removing distributor from whitelist, and then dissalowing the distribution. + */ error DissalowDistOnWhitelistedDistributor(IDistributor distributor, bytes32 distributionId); + /** + * @dev Emitted when a distributor is whitelisted. + * @param distributor The address of the distributor that has been whitelisted. + * @dev Any distribution of the whitelisted distributor MUST be allowed to be installed. + */ event DistributorWhitelisted(IDistributor indexed distributor); + /** + * @notice Emitted when a distributor is removed from the whitelist. + * @param distributor The address of the distributor that was revoked. + * @dev WARNING: After removal, the distributions that were allowed by id are still allowed. + */ event DistributorWhitelistRevoked(IDistributor indexed distributor); + /** + * @dev Emitted when a distribution is allowed by the installer. + * @param distributor The address of the distributor that is allowed. + * @param distributionId The unique identifier of the distribution. + */ event DistributionAllowed(IDistributor indexed distributor, bytes32 indexed distributionId); + /** + * @dev Emitted when a distribution is disallowed by the installer. + * @param distributor The address of the distributor that is disallowed. + * @param distributionId The unique identifier of the distribution that is disallowed. + */ event DistributionDisallowed(IDistributor indexed distributor, bytes32 indexed distributionId); + /** + * @notice Allows a specified distributor to distribute a given distribution ID. + * @param distributor The address of the distributor hosting a distribution Id. + * @param distributionId The ID of the distribution to be allowed. + * @dev MUST emit `DistributionAllowed` event. + */ function allowDistribution(IDistributor distributor, bytes32 distributionId) external; + /** + * @notice Disallows a specific distribution from a given distributor. + * @param distributor The address of the distributor contract. + * @param distributionId The unique identifier of the distribution to be disallowed. + * @dev MUST emit `DistributionDisallowed` event. + */ function disallowDistribution(IDistributor distributor, bytes32 distributionId) external; + /** + * @notice Retrieves the list of whitelisted distributions for a given distributor. + * @param distributor The address of the distributor to query. + * @return An array of bytes32 representing the whitelisted distributions. + * @dev If the distributor is whitelisted, all distributions are allowed. + */ function whitelistedDistributions(IDistributor distributor) external view returns (bytes32[] memory); + /** + * @notice Adds a distributor to the whitelist. + * @param distributor The address of the distributor to be whitelisted. + * @dev After whitelisting, all distributions of the distributor are allowed. Must emit `DistributorWhitelisted` event. + */ function whitelistDistributor(IDistributor distributor) external; + /** + * @notice Revokes the whitelisted status of a given distributor. + * @param distributor The address of the distributor to be revoked. + * @dev After revoking, the distributions that were allowed by id are still allowed. Must emit `DistributorWhitelistRevoked` event. + */ function revokeWhitelistedDistributor(IDistributor distributor) external; + /** + * @notice Checks if the given address is a valid distributor. + * @param distributor The address of the distributor to check. + * @return bool Returns true if the address is a valid distributor, otherwise false. + */ function isDistributor(IDistributor distributor) external view returns (bool); + /** + * @notice Retrieves the list of whitelisted distributor addresses. + * @return An array of addresses that are whitelisted as distributors. + */ function getWhitelistedDistributors() external view returns (address[] memory); + /** + * @dev Emitted when an instance is installed. + * @param instance The address of the installed instance. + * @param distributionId The identifier of the distribution. + * @param permissions The permissions associated with the installation. + * @param args Additional arguments related to the installation. + * @dev MUST be emitted for every new instance installed via `install` function. + */ event Installed(address indexed instance, bytes32 indexed distributionId, bytes32 indexed permissions, bytes args); event Uninstalled(address indexed instance); + /** + * @notice Installs a new instance with the given distributor, distribution ID, and arguments. + * @param distributor The distributor contract to be used for the installation. + * @param distributionId The unique identifier for the distribution. + * @param args Additional arguments required for the installation process. + * @return instanceId The unique identifier of the newly installed instance. + * @dev MUST emit `Installed` event per installed instance. MUST revert if the distributor is not whitelisted or the distribution is not allowed. MUST revert if the distributor is not a valid distributor. + * @dev After succesfull installation ERC77446 hooks SHALL NOT revert if called by target, specifying active instance in `sender` field. + */ function install( IDistributor distributor, bytes32 distributionId, bytes calldata args ) external payable returns (uint256 instanceId); + /** + * @notice Uninstalls an instance with the given ID. + * @param instanceId The unique identifier of the instance to be uninstalled. + * @dev MUST emit `Uninstalled` event per uninstalled instance. MUST revert if the instance is not installed. + * @dev After succesfull uninstallation ERC77446 hooks SHALL revert if called by target, specifying uninstalled instance in `sender` field. + */ function uninstall(uint256 instanceId) external; + /** + * @notice Retrieves the contracts associated with a specific instance. + * @param instanceId The unique identifier of the instance. + * @return instaneContracts An array of addresses representing the contracts of the instance. + */ function getInstance(uint256 instanceId) external view returns (address[] memory instaneContracts); + /** + * @notice Retrieves the number of instances. + * @return The total number of instances as a uint256. + * @dev this number SHALL NOT decrease after uninstallation. + */ function getInstancesNum() external view returns (uint256); + /** + * @notice Checks if the given address is an active instance. + * @param instance The address to check. + * @return bool True if the address is an instance, false otherwise. + */ function isInstance(address instance) external view returns (bool); + /** + * @notice Returns the distributor associated with a given instance. + * @param instance The address of the instance for which the distributor is being queried. + * @return The distributor associated with the specified instance. + */ function distributorOf(address instance) external view returns (IDistributor); + /** + * @notice Retrieves the address of the target contract. + * @return The address of the target contract. + */ function target() external view returns (address); - - error NotAnInstance(address instance); } diff --git a/src/interfaces/IRepository.sol b/src/interfaces/IRepository.sol index 036cb52..16ef5ea 100644 --- a/src/interfaces/IRepository.sol +++ b/src/interfaces/IRepository.sol @@ -1,30 +1,98 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.8; +pragma solidity >=0.8.0 <0.9.0; import "../libraries/LibSemver.sol"; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +/** + * @title IRepository Interface + * @notice It is intended to be implemented by contracts that manage a collection of versions of a byte code. + * @author Peeramid Labs, 2024 + */ interface IRepository is IERC165 { + /** + * @notice Represents a source with version information, a unique identifier, and associated metadata. + * @param version The version of the source, represented using the LibSemver.Version struct. + * @param sourceId A unique identifier for the source. + * @param metadata Additional data associated with the source. + */ struct Source { LibSemver.Version version; bytes32 sourceId; bytes metadata; } + /** + * @notice Error indicating that the specified version does not exist. + * @param version The version number that does not exist. + */ error VersionDoesNotExist(uint256 version); + /** + * @notice Error indicating that a release with a zero value is not allowed. + */ error ReleaseZeroNotAllowed(); + /** + * @notice Error indicating that the specified version already exists. + * @param version The version number that already exists. + */ error VersionExists(uint256 version); + /** + * @notice Error indicating that the version increment is invalid. + * @param version The version number that caused the error. + * @dev The version increment must be exactly one for either major, minor, or patch. + */ error VersionIncrementInvalid(uint256 version); + /** + * @dev Error indicating that the release metadata is empty. + */ error EmptyReleaseMetadata(); - error ReleaseDoesNotExist(); + + /** + * @notice Emitted when a new version is added to the repository. + * @param version The version number of the added item. + * @param source The source identifier of the added item. + * @param buildMetadata Additional metadata related to the build. + */ event VersionAdded(uint256 indexed version, bytes32 indexed source, bytes buildMetadata); + /** + * @notice Emitted when the metadata of a release is updated. + * @param version The version number of the release. + * @param releaseMetadata The metadata associated with the release. + */ event ReleaseMetadataUpdated(uint256 indexed version, bytes releaseMetadata); + /** + * @notice Updates the metadata for a specific release version. + * @param version The version of the release to update. + * @param releaseMetadata The new metadata to associate with the release. + * @dev It MUST emit `ReleaseMetadataUpdated` event. + */ function updateReleaseMetadata(LibSemver.Version memory version, bytes calldata releaseMetadata) external; + /** + * @notice Retrieves the name of the repository. + * @return The name of the repository as a bytes32 value. + */ function repositoryName() external view returns (bytes32); + /** + * @notice Creates a new release for the given source ID. + * @param sourceId The unique identifier of the source. + * @param metadata The metadata associated with the release. + * @param version The semantic version of the new release. + * @dev It MUST emit `VersionAdded` event. + */ function newRelease(bytes32 sourceId, bytes memory metadata, LibSemver.Version memory version) external; + /** + * @notice Retrieves the latest source. + * @return The requested source + */ function getLatest() external view returns (Source memory); + /** + * @notice Retrieves a specific item from the repository. + * @param baseVersion the base of required version + * @param requirement the requirement of the version + * @return The requested `Source`. + */ function get( - LibSemver.Version calldata version, + LibSemver.Version calldata baseVersion, LibSemver.requirements requirement ) external view returns (Source memory); } diff --git a/src/libraries/LibMiddleware.sol b/src/libraries/LibMiddleware.sol index 2f22bf3..34a4b35 100644 --- a/src/libraries/LibMiddleware.sol +++ b/src/libraries/LibMiddleware.sol @@ -21,9 +21,7 @@ library LibMiddleware { address layerAddress, uint256 layerIndex, bytes memory layerConfigData - ) internal // bytes4 beforeCallMethodSignature, - // bytes4 afterCallMethodSignature - { + ) internal { LayerStruct[] storage ls = accessLayersStorage(); ls[layerIndex].layerAddess = layerAddress; ls[layerIndex].layerConfigData = layerConfigData;