diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34a4a52..762a296 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,8 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly - name: Show Forge version run: | diff --git a/foundry.toml b/foundry.toml index 50a5022..56c1407 100644 --- a/foundry.toml +++ b/foundry.toml @@ -10,5 +10,8 @@ remappings = [ '@diamond-logs/=src/libraries/logs/', '@diamond-errors/=src/libraries/errors/' ] -optimizer_runs = 20_000 -ffi = true +optimizer_runs = 999_999 +via_ir = false + +[fmt] +sort_imports = true diff --git a/script/DeployDiamond.s.sol b/script/DeployDiamond.s.sol index fc4ab90..3853cd5 100644 --- a/script/DeployDiamond.s.sol +++ b/script/DeployDiamond.s.sol @@ -1,14 +1,15 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; -import {Script} from "forge-std/Script.sol"; -import {Diamond} from "@diamond/Diamond.sol"; +import {FacetCut, FacetCutAction} from "@diamond-storage/DiamondStorage.sol"; +import {GetSelectors} from "@diamond-test/helpers/GetSelectors.sol"; +import {MockDiamond} from "@diamond-test/mocks/MockDiamond.sol"; import {DiamondCutFacet} from "@diamond/facets/DiamondCutFacet.sol"; import {DiamondLoupeFacet} from "@diamond/facets/DiamondLoupeFacet.sol"; import {OwnableRolesFacet} from "@diamond/facets/OwnableRolesFacet.sol"; -import {ERC165Init} from "@diamond/initializers/ERC165Init.sol"; -import {FacetCutAction, FacetCut, DiamondArgs} from "@diamond-storage/DiamondStorage.sol"; -import {GetSelectors} from "@diamond-test/helpers/GetSelectors.sol"; +import {DiamondInit} from "@diamond/initializers/DiamondInit.sol"; +import {LibContext} from "@diamond/libraries/LibContext.sol"; +import {Script} from "forge-std/Script.sol"; /// @title DeployDiamond /// @notice Deployment script for an EIP-2535 Diamond proxy contract with core facets and ERC165 initialization @@ -28,14 +29,7 @@ contract DeployDiamond is Script, GetSelectors { OwnableRolesFacet ownableRolesFacet = new OwnableRolesFacet(); // Deploy ERC165 initializer contract - ERC165Init erc165Init = new ERC165Init(); - - // Prepare DiamondArgs: owner and init data - DiamondArgs memory args = DiamondArgs({ - owner: msg.sender, - init: address(erc165Init), - initData: abi.encodeWithSignature("initErc165()") - }); + address diamondInit = address(new DiamondInit()); // Create an array of FacetCut entries for standard facets FacetCut[] memory cut = new FacetCut[](3); @@ -62,7 +56,8 @@ contract DeployDiamond is Script, GetSelectors { }); // Deploy the Diamond contract with the facets and initialization args - Diamond diamond = new Diamond(cut, args); + MockDiamond diamond = + new MockDiamond(cut, diamondInit, abi.encodeWithSignature("initDiamond(address)", LibContext._msgSender())); diamond_ = address(diamond); vm.stopBroadcast(); } diff --git a/src/Diamond.sol b/src/Diamond.sol index 8dbd8ab..afd6c64 100644 --- a/src/Diamond.sol +++ b/src/Diamond.sol @@ -1,71 +1,74 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; +import {FacetCut} from "@diamond-storage/DiamondStorage.sol"; import {LibDiamond} from "@diamond/libraries/LibDiamond.sol"; -import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; -import {DiamondArgs, FacetCut} from "@diamond-storage/DiamondStorage.sol"; -import {FunctionDoesNotExist} from "@diamond-errors/DiamondErrors.sol"; /* -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⡤⣶⣾⠟⠋⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠛⠛⠓⠶⠤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⢠⣴⠶⠛⠿⢿⣶⣿⡋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠓⠲⢦⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⢀⣴⡾⠋⠀⠀⢀⣴⠟⠁⠈⠛⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠲⢶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⣠⣶⡿⠋⠀⠀⠀⣠⠟⠁⠀⠀⠀⣀⣨⣿⣶⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⣰⣾⡿⠋⠀⠀⣀⣤⣾⣥⠶⠒⠚⠋⠉⠉⠁⠀⠘⣧⠈⠉⠛⠒⠶⣤⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣝⠷⣤⡀⠀⠀⠀⠀⠀⠀⠀ -⣿⡿⠒⠛⠋⢉⡽⠋⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣧⠀⠀⠀⠀⠀⠀⠉⠉⠛⠒⢶⣤⣤⣤⣤⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⠶⠛⠙⣟⠚⠻⢶⣄⠀⠀⠀⠀⠀ -⢻⣷⡀⢀⣴⠏⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⡆⠀⠀⠀⣀⣤⣤⠴⠖⠚⠋⠉⠛⢦⣄⡀⠀⠉⠉⠉⠉⠉⠉⠉⠉⣹⡿⠿⣥⡀⠀⠀⠀⠹⣆⠀⠀⠹⣧⡀⠀⠀⠀ -⠈⢿⡹⣿⡁⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡴⣿⣶⠛⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢦⣄⠀⠀⠀⠀⠀⢀⡾⠋⠀⠀⠈⠙⠳⣦⡀⠀⢻⡄⠀⠀⠹⣷⡀⠀⠀ -⠀⠈⣷⣬⣿⣦⣄⠀⣿⠀⣀⣠⣤⡴⠖⠚⠋⠉⠁⠀⠀⡏⠈⢷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⣦⡀⢀⣴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠉⠳⢦⣷⡀⠀⠀⠹⣿⡄⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⡤⣶⣾⠟⠋⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠛⠛⠓⠶⠤⣤⣀⡀ +⠀⠀⠀⠀⠀⠀⠀⢠⣴⠶⠛⠿⢿⣶⣿⡋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠓⠲⢦⣤⣀⣀ +⠀⠀⠀⠀⢀⣴⡾⠋⠀⠀⢀⣴⠟⠁⠈⠛⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠲⢶⣄ +⠀⠀⣠⣶⡿⠋⠀⠀⠀⣠⠟⠁⠀⠀⠀⣀⣨⣿⣶⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⣦⡀ +⣰⣾⡿⠋⠀⠀⣀⣤⣾⣥⠶⠒⠚⠋⠉⠉⠁⠀⠘⣧⠈⠉⠛⠒⠶⣤⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣝⠷⣤⡀ +⣿⡿⠒⠛⠋⢉⡽⠋⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣧⠀⠀⠀⠀⠀⠀⠉⠉⠛⠒⢶⣤⣤⣤⣤⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⠶⠛⠙⣟⠚⠻⢶⣄ +⢻⣷⡀⢀⣴⠏⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⡆⠀⠀⠀⣀⣤⣤⠴⠖⠚⠋⠉⠛⢦⣄⡀⠀⠉⠉⠉⠉⠉⠉⠉⠉⣹⡿⠿⣥⡀⠀⠀⠀⠹⣆⠀⠀⠹⣧⡀ +⠈⢿⡹⣿⡁⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡴⣿⣶⠛⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢦⣄⠀⠀⠀⠀⠀⢀⡾⠋⠀⠀⠈⠙⠳⣦⡀⠀⢻⡄⠀⠀⠹⣷⡀ +⠀⠈⣷⣬⣿⣦⣄⠀⣿⠀⣀⣠⣤⡴⠖⠚⠋⠉⠁⠀⠀⡏⠈⢷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⣦⡀⢀⣴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠉⠳⢦⣷⡀⠀⠀⠹⣿⡄ ⠀⠀⠈⠻⣆⠉⠙⣿⡙⠻⢯⣀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠃⠀⠀⠙⢷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡿⡿⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡟⢿⢦⣄⠀⠹⣿⣆ ⠀⠀⠀⠀⠈⢷⣄⠀⠙⢷⣄⣉⡻⣶⣄⣀⠀⠀⠀⢀⣿⠀⠀⠀⠀⠀⠻⣦⡀⠀⠀⠀⠀⠀⠀⢀⣠⡴⠞⠉⠀⢸⡇⠀⠙⠳⣦⡀⠀⠀⠀⠀⠀⠀⠀⣾⠀⠈⣧⠈⠙⢦⣽⣼ ⠀⠀⠀⠀⠀⠀⠙⢧⡄⠀⠻⣯⡉⠉⠉⠛⢿⣍⡉⠉⠙⠷⣤⣀⠀⠀⠀⠈⠻⣦⠀⠀⣀⣤⠶⠋⠁⠀⠀⠀⠀⢸⠃⠀⠀⠀⠀⠙⠷⣤⡀⠀⠀⠀⣼⠃⠀⠀⠘⣦⠀⢀⣬⡿ -⠀⠀⠀⠀⠀⠀⠀⠀⠻⣦⡀⢹⣷⣄⠀⠀⠀⠈⠙⢶⡶⠶⠶⠯⣭⣛⣓⡲⠶⠾⠷⠿⣭⣀⣀⠀⠀⠀⠀⠀⣀⣾⠀⠀⠀⠀⠀⠀⠀⠈⠛⢶⣄⣰⠏⠀⠀⢀⣠⡿⢟⡿⠋⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣄⢻⣟⢷⡄⠀⠀⠀⠈⢷⠀⠀⠀⠀⠀⠈⠉⠓⢶⣦⠴⠶⠾⠭⣭⣍⣉⣉⠉⠉⠉⠙⠳⢶⣤⣄⣤⡴⠶⠖⠛⠋⣙⣿⠷⠿⠿⣧⣶⠟⠁⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢷⣽⣆⠻⣦⡀⠀⠀⠘⣇⠀⠀⠀⠀⠀⠀⠀⣼⣷⠀⠀⠀⠀⠀⠀⠈⠉⢛⣶⠶⠛⠉⠉⠁⠀⠉⠙⣳⣶⠞⠋⠉⠀⣀⡶⠾⠋⠁⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣎⠌⢳⣄⠀⠀⢹⣆⠀⠀⠀⠀⠀⣼⠃⣿⠀⠀⠀⠀⠀⠀⠀⣠⠟⠁⠀⠀⠀⠀⠀⣠⣴⡿⠛⠁⢀⣤⠶⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣦⡀⠙⣧⡀⠀⢻⡄⠀⠀⠀⣸⠇⠀⣿⠀⠀⠀⠀⠀⢠⡾⠃⠀⠀⠀⢀⣤⠾⣫⡿⢋⣀⡴⠞⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣆⠈⠻⣆⠈⣷⠀⠀⣰⡏⠀⠀⢻⠀⠀⠀⢀⣴⠏⠀⠀⣀⣴⠞⠋⣠⣾⣿⠾⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢷⣄⠘⢷⡼⣇⢀⡟⠀⠀⠀⢸⠀⠀⣠⠟⠁⣠⡴⠞⠉⢀⣠⡾⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣦⡀⠹⣿⡾⠁⠀⠀⠀⢸⣀⣾⣣⠶⠛⠁⣀⣤⠾⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣄⠈⢿⡀⠀⠀⢀⣼⠟⠋⢀⣠⠴⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠷⣜⣧⠀⣠⠟⣁⣴⠞⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣾⠿⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠻⣦⡀⢹⣷⣄⠀⠀⠀⠈⠙⢶⡶⠶⠶⠯⣭⣛⣓⡲⠶⠾⠷⠿⣭⣀⣀⠀⠀⠀⠀⠀⣀⣾⠀⠀⠀⠀⠀⠀⠀⠈⠛⢶⣄⣰⠏⠀⠀⢀⣠⡿⢟⡿⠋ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣄⢻⣟⢷⡄⠀⠀⠀⠈⢷⠀⠀⠀⠀⠀⠈⠉⠓⢶⣦⠴⠶⠾⠭⣭⣍⣉⣉⠉⠉⠉⠙⠳⢶⣤⣄⣤⡴⠶⠖⠛⠋⣙⣿⠷⠿⠿⣧⣶⠟⠁ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢷⣽⣆⠻⣦⡀⠀⠀⠘⣇⠀⠀⠀⠀⠀⠀⠀⣼⣷⠀⠀⠀⠀⠀⠀⠈⠉⢛⣶⠶⠛⠉⠉⠁⠀⠉⠙⣳⣶⠞⠋⠉⠀⣀⡶⠾⠋⠁ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣎⠌⢳⣄⠀⠀⢹⣆⠀⠀⠀⠀⠀⣼⠃⣿⠀⠀⠀⠀⠀⠀⠀⣠⠟⠁⠀⠀⠀⠀⠀⣠⣴⡿⠛⠁⢀⣤⠶⠛⠉ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣦⡀⠙⣧⡀⠀⢻⡄⠀⠀⠀⣸⠇⠀⣿⠀⠀⠀⠀⠀⢠⡾⠃⠀⠀⠀⢀⣤⠾⣫⡿⢋⣀⡴⠞⠉ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣆⠈⠻⣆⠈⣷⠀⠀⣰⡏⠀⠀⢻⠀⠀⠀⢀⣴⠏⠀⠀⣀⣴⠞⠋⣠⣾⣿⠾⠛⠁ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢷⣄⠘⢷⡼⣇⢀⡟⠀⠀⠀⢸⠀⠀⣠⠟⠁⣠⡴⠞⠉⢀⣠⡾⠟⠋ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣦⡀⠹⣿⡾⠁⠀⠀⠀⢸⣀⣾⣣⠶⠛⠁⣀⣤⠾⠋⠁ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣄⠈⢿⡀⠀⠀⢀⣼⠟⠋⢀⣠⠴⠛⠉ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠷⣜⣧⠀⣠⠟⣁⣴⠞⠋⠁ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣾⠿⠛⠉ */ /// @title Diamond -/// @notice Implements EIP-2535 Diamond proxy pattern, allowing dynamic addition, replacement, and removal of facets -/// @author David Dada -/// @author Modified from Nick Mudge (https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/Diamond.sol) -/// @author Modified from Timo (https://github.com/FydeTreasury/Diamond-Foundry/blob/main/src/Diamond.sol) -/// -/// @dev Uses LibDiamond for facet management and LibOwnableRoles for ownership initialization -contract Diamond { +/// @notice Implements ERC-2535 Diamond proxy pattern, allowing dynamic addition, replacement, and removal of facets +/// @author Nick Mudge (https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/Diamond.sol) +/// @author Modified by David Dada (https://github.com/dadadave80) +abstract contract Diamond { /// @notice Initializes the Diamond proxy with the provided facets and initialization parameters - /// @param _diamondCut Array of FacetCut structs defining facet addresses, corresponding function selectors, and actions (Add, Replace, Remove) - /// @param _args Struct containing the initial owner address, optional init contract address, and init calldata - constructor(FacetCut[] memory _diamondCut, DiamondArgs memory _args) payable { - LibOwnableRoles._initializeOwner(_args.owner); - LibDiamond._diamondCut(_diamondCut, _args.init, _args.initData); + /// @param _init Address of the initialization contract + /// @param _calldata Calldata to be passed to the initialization contract + constructor(FacetCut[] memory _facetCuts, address _init, bytes memory _calldata) payable { + _diamondCut(_facetCuts, _init, _calldata); } /// @notice Receive function to accept plain Ether transfers /// @dev Allows contract to receive Ether without data - receive() external payable {} + receive() external payable virtual {} /// @notice Fallback function that delegates calls to the appropriate facet based on function selector /// @dev Reads the facet address from diamond storage and performs a delegatecall; reverts if selector is not found - fallback() external payable { - // Lookup facet for function selector - address facet = LibDiamond._diamondStorage().selectorToFacetAndPosition[msg.sig].facetAddress; - if (facet == address(0)) revert FunctionDoesNotExist(msg.sig); + fallback() external payable virtual { + _beforeDelegate(); + _delegate(_implementation()); + } + + /// @notice Retrieves the implementation address for the current function call + /// @dev Implementation and Facet are synonyms in a Diamond Proxy + function _implementation() internal virtual returns (address) { + return LibDiamond._selectorToFacet(msg.sig); + } + /// @notice Internal function to perform a delegatecall to an implementation + /// @param _facet Address of the implementation to delegate to + function _delegate(address _facet) internal virtual { assembly { // Copy calldata to memory calldatacopy(0, 0, calldatasize()) - // Delegate call to facet - let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) + // Delegate call to implementation + let result := delegatecall(gas(), _facet, 0, calldatasize(), 0, 0) // Copy returned data returndatacopy(0, 0, returndatasize()) @@ -76,4 +79,13 @@ contract Diamond { default { return(0, returndatasize()) } } } + + /// @notice Internal function to perform a diamond cut + function _diamondCut(FacetCut[] memory _facetCuts, address _init, bytes memory _calldata) internal virtual { + LibDiamond._diamondCut(_facetCuts, _init, _calldata); + } + + /// @notice Internal hook function to run before a delegatecall to the facet + /// @dev This function can be replaced to perform additional logic before the delegatecall + function _beforeDelegate() internal virtual; } diff --git a/src/facets/DiamondCutFacet.sol b/src/facets/DiamondCutFacet.sol index 7d4f1d4..48f518d 100644 --- a/src/facets/DiamondCutFacet.sol +++ b/src/facets/DiamondCutFacet.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; +import {FacetCut} from "@diamond-storage/DiamondStorage.sol"; import {IDiamondCut} from "@diamond/interfaces/IDiamondCut.sol"; import {LibDiamond} from "@diamond/libraries/LibDiamond.sol"; import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; -import {FacetCut} from "@diamond-storage/DiamondStorage.sol"; /// @title DiamondCutFacet /// @notice Simple single owner and multiroles authorization mixin. @@ -26,6 +26,6 @@ contract DiamondCutFacet is IDiamondCut { // Check that the caller is the owner LibOwnableRoles._checkOwner(); // Call the diamond cut function from the library - LibDiamond._diamondCut(_diamondCut, _init, _calldata); + LibDiamond._diamondCutCalldata(_diamondCut, _init, _calldata); } } diff --git a/src/facets/DiamondLoupeFacet.sol b/src/facets/DiamondLoupeFacet.sol index 2ff56a1..9653ed7 100644 --- a/src/facets/DiamondLoupeFacet.sol +++ b/src/facets/DiamondLoupeFacet.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; -import {DiamondStorage, LibDiamond} from "@diamond/libraries/LibDiamond.sol"; -import {IDiamondLoupe} from "@diamond/interfaces/IDiamondLoupe.sol"; import {Facet} from "@diamond-storage/DiamondStorage.sol"; +import {IDiamondLoupe} from "@diamond/interfaces/IDiamondLoupe.sol"; +import {DiamondStorage, LibDiamond} from "@diamond/libraries/LibDiamond.sol"; /// @title DiamondLoupeFacet /// @notice Provides read-only functions to inspect the state of a Diamond proxy, including facets, function selectors, and supported interfaces diff --git a/src/facets/OwnableRolesFacet.sol b/src/facets/OwnableRolesFacet.sol index 87e3489..d7dc23c 100644 --- a/src/facets/OwnableRolesFacet.sol +++ b/src/facets/OwnableRolesFacet.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; @@ -72,18 +72,18 @@ contract OwnableRolesFacet { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. - function owner() public view returns (address result_) { - result_ = LibOwnableRoles._owner(); + function owner() public view returns (address) { + return LibOwnableRoles._owner(); } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. - function ownershipHandoverExpiresAt(address _pendingOwner) public view returns (uint256 result_) { - result_ = _pendingOwner._ownershipHandoverExpiresAt(); + function ownershipHandoverExpiresAt(address _pendingOwner) public view returns (uint256) { + return _pendingOwner._ownershipHandoverExpiresAt(); } /// @dev Returns the roles of `user`. - function rolesOf(address _user) public view returns (uint256 roles_) { - roles_ = _user._rolesOf(); + function rolesOf(address _user) public view returns (uint256) { + return _user._rolesOf(); } /// @dev Returns whether `user` has any of `roles`. @@ -99,24 +99,15 @@ contract OwnableRolesFacet { /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. - function rolesFromOrdinals(uint8[] memory ordinals) external pure returns (uint256 roles_) { - roles_ = ordinals._rolesFromOrdinals(); + function rolesFromOrdinals(uint8[] memory ordinals) external pure returns (uint256) { + return ordinals._rolesFromOrdinals(); } /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. - function ordinalsFromRoles(uint256 roles) external pure returns (uint8[] memory ordinals_) { - ordinals_ = roles._ordinalsFromRoles(); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev This override returns true to make `_initializeOwner` prevent double-initialization. - function _guardInitializeOwner() internal pure returns (bool guard) { - return true; + function ordinalsFromRoles(uint256 roles) external pure returns (uint8[] memory) { + return roles._ordinalsFromRoles(); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ diff --git a/src/initializers/ERC165Init.sol b/src/initializers/DiamondInit.sol similarity index 73% rename from src/initializers/ERC165Init.sol rename to src/initializers/DiamondInit.sol index a0b47e9..541b9e7 100644 --- a/src/initializers/ERC165Init.sol +++ b/src/initializers/DiamondInit.sol @@ -1,19 +1,23 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; import {DiamondStorage, LibDiamond} from "@diamond/libraries/LibDiamond.sol"; +import {LibOwnableRoles} from "@diamond/libraries/LibOwnableRoles.sol"; -/// @title ERC165Init +/// @title DiamondInit /// @notice Provides an initializer to register standard interface support (ERC-165, ERC-173, IDiamondCut, IDiamondLoupe) /// @author David Dada /// @author Modified from Nick Mudge (https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/upgradeInitializers/DiamondInit.sol) /// -/// @dev Intended to be called as the `initERC165` function in a diamond cut to set up ERC-165 interface IDs -contract ERC165Init { +/// @dev Intended to be called as the `initDiamond` function in a diamond cut to set up ERC-165 interface IDs +contract DiamondInit { /// @notice Initialize the contract with the ERC165 interface support. /// @dev This function is called during the diamond cut process to set up /// the initial state of the contract. - function initErc165() public { + function initDiamond(address _owner) public { + // Initialize the owner + LibOwnableRoles._initializeOwner(_owner); + DiamondStorage storage ds = LibDiamond._diamondStorage(); /// @dev type(ERC165).interfaceId ds.supportedInterfaces[0x01ffc9a7] = true; diff --git a/src/initializers/MultiInit.sol b/src/initializers/MultiInit.sol deleted file mode 100644 index 973e9c9..0000000 --- a/src/initializers/MultiInit.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {LibDiamond} from "@diamond/libraries/LibDiamond.sol"; -import {AddressAndCalldataLengthDoNotMatch} from "@diamond-errors/DiamondErrors.sol"; - -/// @title MultiInit -/// @notice Executes multiple initialization calls in sequence during a diamond upgrade -/// @author David Dada -/// @author Modified from Timo (https://github.com/FydeTreasury/Diamond-Foundry/blob/main/src/upgradeInitializers/DiamondMultiInit.sol) -/// -/// @dev Useful when a diamond cut requires initializing several facets at once -contract MultiInit { - /// @notice Performs multiple initialization calls to provided addresses with corresponding calldata - /// @dev Reverts if `_addresses.length != _calldata.length`. Each address is called via delegatecall using LibDiamond._initializeDiamondCut. - /// @param _addresses The list of initializer contract addresses - /// @param _calldata The list of encoded function calls for each initializer - function multiInit(address[] calldata _addresses, bytes[] calldata _calldata) public { - uint256 addressesLength = _addresses.length; - if (addressesLength != _calldata.length) { - revert AddressAndCalldataLengthDoNotMatch(); - } - for (uint256 i; i < addressesLength; ++i) { - LibDiamond._initializeDiamondCut(_addresses[i], _calldata[i]); - } - } -} diff --git a/src/interfaces/IDiamondCut.sol b/src/interfaces/IDiamondCut.sol index 6be7c33..23a7ab8 100644 --- a/src/interfaces/IDiamondCut.sol +++ b/src/interfaces/IDiamondCut.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; import {FacetCut} from "@diamond-storage/DiamondStorage.sol"; diff --git a/src/interfaces/IDiamondLoupe.sol b/src/interfaces/IDiamondLoupe.sol index 9be0ab7..823bb36 100644 --- a/src/interfaces/IDiamondLoupe.sol +++ b/src/interfaces/IDiamondLoupe.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; import {Facet} from "@diamond-storage/DiamondStorage.sol"; diff --git a/src/libraries/LibContext.sol b/src/libraries/LibContext.sol new file mode 100644 index 0000000..1e4bd0d --- /dev/null +++ b/src/libraries/LibContext.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +library LibContext { + function _msgSender() internal view returns (address) { + return msg.sender; + } + + function _msgData() internal pure returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal pure returns (uint256) { + return 0; + } +} diff --git a/src/libraries/LibDiamond.sol b/src/libraries/LibDiamond.sol index 36061b9..178e338 100644 --- a/src/libraries/LibDiamond.sol +++ b/src/libraries/LibDiamond.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; -import {FacetCut, FacetCutAction, DiamondStorage, DIAMOND_STORAGE_LOCATION} from "@diamond-storage/DiamondStorage.sol"; +import {DIAMOND_STORAGE_LOCATION, DiamondStorage, FacetCut, FacetCutAction} from "@diamond-storage/DiamondStorage.sol"; import {DiamondCut} from "@diamond/libraries/logs/DiamondLogs.sol"; /// forge-lint: disable-next-line(unaliased-plain-import) import "@diamond-errors/DiamondErrors.sol"; /// @title LibDiamond -/// @notice Internal library providing core functionality for EIP-2535 Diamond proxy management. +/// @notice Internal library providing core functionality for ERC-2535 Diamond proxy management. /// @author David Dada /// @author Modified from Nick Mudge (https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/libraries/LibDiamond.sol) /// @@ -34,42 +34,39 @@ library LibDiamond { /// @param _init The address of the contract or facet to execute `data`. /// @param _calldata A function call, including function selector and arguments. function _diamondCut(FacetCut[] memory _facetCuts, address _init, bytes memory _calldata) internal { + DiamondStorage storage ds = _diamondStorage(); uint256 facetCutsLength = _facetCuts.length; - if (facetCutsLength == 0) revert NoFacetsInDiamondCut(); - for (uint256 facetIndex; facetIndex < facetCutsLength; ++facetIndex) { - FacetCutAction action = _facetCuts[facetIndex].action; - if (action == FacetCutAction.Add) { - _addFunctions(_facetCuts[facetIndex].facetAddress, _facetCuts[facetIndex].functionSelectors); - } else if (action == FacetCutAction.Replace) { - _replaceFunctions(_facetCuts[facetIndex].facetAddress, _facetCuts[facetIndex].functionSelectors); - } else if (action == FacetCutAction.Remove) { - _removeFunctions(_facetCuts[facetIndex].facetAddress, _facetCuts[facetIndex].functionSelectors); + for (uint256 i; i < facetCutsLength; ++i) { + if (_facetCuts[i].action == FacetCutAction.Add) { + _addFunctions(ds, _facetCuts[i].facetAddress, _facetCuts[i].functionSelectors); + } else if (_facetCuts[i].action == FacetCutAction.Replace) { + _replaceFunctions(ds, _facetCuts[i].facetAddress, _facetCuts[i].functionSelectors); } else { - revert IncorrectFacetCutAction(uint8(action)); + _removeFunctions(ds, _facetCuts[i].facetAddress, _facetCuts[i].functionSelectors); } } - emit DiamondCut(_facetCuts, _init, _calldata); + _initializeDiamondCut(_init, _calldata); + emit DiamondCut(_facetCuts, _init, _calldata); } /// @dev Add functions to the diamond. /// @param _facetAddress The address of the facet to add functions to. /// @param _functionSelectors The function selectors to add to the facet. - function _addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { + function _addFunctions(DiamondStorage storage _ds, address _facetAddress, bytes4[] memory _functionSelectors) + internal + { uint256 functionSelectorsLength = _functionSelectors.length; if (functionSelectorsLength == 0) revert NoSelectorsGivenToAdd(); if (_facetAddress == address(0)) revert CannotAddSelectorsToZeroAddress(_functionSelectors); - DiamondStorage storage ds = _diamondStorage(); - uint96 selectorPosition = uint96(ds.facetToSelectorsAndPosition[_facetAddress].functionSelectors.length); + uint96 selectorPosition = uint96(_facetToSelectors(_ds, _facetAddress).length); // Add new facet address if it does not exist - if (selectorPosition == 0) { - _addFacet(ds, _facetAddress); - } - for (uint256 selectorIndex; selectorIndex < functionSelectorsLength; ++selectorIndex) { - bytes4 selector = _functionSelectors[selectorIndex]; - address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; + if (selectorPosition == 0) _addFacet(_ds, _facetAddress); + for (uint256 i; i < functionSelectorsLength; ++i) { + bytes4 selector = _functionSelectors[i]; + address oldFacetAddress = _selectorToFacet(_ds, selector); if (oldFacetAddress != address(0)) revert CannotAddFunctionToDiamondThatAlreadyExists(selector); - _addFunction(ds, selector, selectorPosition, _facetAddress); + _addFunction(_ds, selector, selectorPosition, _facetAddress); ++selectorPosition; } } @@ -77,24 +74,23 @@ library LibDiamond { /// @dev Replace functions in the diamond. /// @param _facetAddress The address of the facet to replace functions from. /// @param _functionSelectors The function selectors to replace in the facet. - function _replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { + function _replaceFunctions(DiamondStorage storage _ds, address _facetAddress, bytes4[] memory _functionSelectors) + internal + { uint256 functionSelectorsLength = _functionSelectors.length; if (functionSelectorsLength == 0) revert NoSelectorsGivenToAdd(); if (_facetAddress == address(0)) revert CannotAddSelectorsToZeroAddress(_functionSelectors); - DiamondStorage storage ds = _diamondStorage(); - uint96 selectorPosition = uint96(ds.facetToSelectorsAndPosition[_facetAddress].functionSelectors.length); + uint96 selectorPosition = uint96(_facetToSelectors(_ds, _facetAddress).length); // add new facet address if it does not exist - if (selectorPosition == 0) { - _addFacet(ds, _facetAddress); - } - for (uint256 selectorIndex; selectorIndex < functionSelectorsLength; ++selectorIndex) { - bytes4 selector = _functionSelectors[selectorIndex]; - address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; + if (selectorPosition == 0) _addFacet(_ds, _facetAddress); + for (uint256 i; i < functionSelectorsLength; ++i) { + bytes4 selector = _functionSelectors[i]; + address oldFacetAddress = _selectorToFacet(_ds, selector); if (oldFacetAddress == _facetAddress) { revert CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(selector); } - _removeFunction(ds, oldFacetAddress, selector); - _addFunction(ds, selector, selectorPosition, _facetAddress); + _removeFunction(_ds, oldFacetAddress, selector); + _addFunction(_ds, selector, selectorPosition, _facetAddress); ++selectorPosition; } } @@ -102,15 +98,109 @@ library LibDiamond { /// @dev Remove functions from the diamond. /// @param _facetAddress The address of the facet to remove functions from. /// @param _functionSelectors The function selectors to remove from the facet. - function _removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { + function _removeFunctions(DiamondStorage storage _ds, address _facetAddress, bytes4[] memory _functionSelectors) + internal + { uint256 functionSelectorsLength = _functionSelectors.length; if (_facetAddress != address(0)) revert RemoveFacetAddressMustBeZeroAddress(_facetAddress); if (functionSelectorsLength == 0) revert NoSelectorsProvidedForFacetForCut(_facetAddress); + for (uint256 i; i < functionSelectorsLength; ++i) { + bytes4 selector = _functionSelectors[i]; + address oldFacetAddress = _selectorToFacet(_ds, selector); + _removeFunction(_ds, oldFacetAddress, selector); + } + } + + //*////////////////////////////////////////////////////////////////////////// + // CALLDATA DIAMOND CUT + //////////////////////////////////////////////////////////////////////////*// + + /// @dev Add/replace/remove any number of functions and optionally execute + /// a function with delegatecall. + /// @param _facetCuts Contains the facet addresses, cut actions and function selectors. + /// @param _init The address of the contract or facet to execute `data`. + /// @param _calldata A function call, including function selector and arguments. + function _diamondCutCalldata(FacetCut[] calldata _facetCuts, address _init, bytes calldata _calldata) internal { DiamondStorage storage ds = _diamondStorage(); - for (uint256 selectorIndex; selectorIndex < functionSelectorsLength; ++selectorIndex) { - bytes4 selector = _functionSelectors[selectorIndex]; - address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; - _removeFunction(ds, oldFacetAddress, selector); + uint256 facetCutsLength = _facetCuts.length; + for (uint256 i; i < facetCutsLength; ++i) { + if (_facetCuts[i].action == FacetCutAction.Add) { + _addFunctionsCalldata(ds, _facetCuts[i].facetAddress, _facetCuts[i].functionSelectors); + } else if (_facetCuts[i].action == FacetCutAction.Replace) { + _replaceFunctionsCalldata(ds, _facetCuts[i].facetAddress, _facetCuts[i].functionSelectors); + } else { + _removeFunctionsCalldata(ds, _facetCuts[i].facetAddress, _facetCuts[i].functionSelectors); + } + } + + _initializeDiamondCutCalldata(_init, _calldata); + emit DiamondCut(_facetCuts, _init, _calldata); + } + + /// @dev Add functions to the diamond. + /// @param _facetAddress The address of the facet to add functions to. + /// @param _functionSelectors The function selectors to add to the facet. + function _addFunctionsCalldata( + DiamondStorage storage _ds, + address _facetAddress, + bytes4[] calldata _functionSelectors + ) internal { + uint256 functionSelectorsLength = _functionSelectors.length; + if (functionSelectorsLength == 0) revert NoSelectorsGivenToAdd(); + if (_facetAddress == address(0)) revert CannotAddSelectorsToZeroAddress(_functionSelectors); + uint96 selectorPosition = uint96(_facetToSelectors(_ds, _facetAddress).length); + // Add new facet address if it does not exist + if (selectorPosition == 0) _addFacet(_ds, _facetAddress); + for (uint256 i; i < functionSelectorsLength; ++i) { + bytes4 selector = _functionSelectors[i]; + address oldFacetAddress = _selectorToFacet(_ds, selector); + if (oldFacetAddress != address(0)) revert CannotAddFunctionToDiamondThatAlreadyExists(selector); + _addFunction(_ds, selector, selectorPosition, _facetAddress); + ++selectorPosition; + } + } + + /// @dev Replace functions in the diamond. + /// @param _facetAddress The address of the facet to replace functions from. + /// @param _functionSelectors The function selectors to replace in the facet. + function _replaceFunctionsCalldata( + DiamondStorage storage _ds, + address _facetAddress, + bytes4[] calldata _functionSelectors + ) internal { + uint256 functionSelectorsLength = _functionSelectors.length; + if (functionSelectorsLength == 0) revert NoSelectorsGivenToAdd(); + if (_facetAddress == address(0)) revert CannotAddSelectorsToZeroAddress(_functionSelectors); + uint96 selectorPosition = uint96(_facetToSelectors(_ds, _facetAddress).length); + // add new facet address if it does not exist + if (selectorPosition == 0) _addFacet(_ds, _facetAddress); + for (uint256 i; i < functionSelectorsLength; ++i) { + bytes4 selector = _functionSelectors[i]; + address oldFacetAddress = _selectorToFacet(_ds, selector); + if (oldFacetAddress == _facetAddress) { + revert CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(selector); + } + _removeFunction(_ds, oldFacetAddress, selector); + _addFunction(_ds, selector, selectorPosition, _facetAddress); + ++selectorPosition; + } + } + + /// @dev Remove functions from the diamond. + /// @param _facetAddress The address of the facet to remove functions from. + /// @param _functionSelectors The function selectors to remove from the facet. + function _removeFunctionsCalldata( + DiamondStorage storage _ds, + address _facetAddress, + bytes4[] calldata _functionSelectors + ) internal { + uint256 functionSelectorsLength = _functionSelectors.length; + if (_facetAddress != address(0)) revert RemoveFacetAddressMustBeZeroAddress(_facetAddress); + if (functionSelectorsLength == 0) revert NoSelectorsProvidedForFacetForCut(_facetAddress); + for (uint256 i; i < functionSelectorsLength; ++i) { + bytes4 selector = _functionSelectors[i]; + address oldFacetAddress = _selectorToFacet(_ds, selector); + _removeFunction(_ds, oldFacetAddress, selector); } } @@ -128,9 +218,12 @@ library LibDiamond { /// @param _selector The function selector to add. /// @param _selectorPosition The position of the function selector in the facetToSelectorsAndPosition.functionSelectors array. /// @param _facetAddress The address of the facet to add the function selector to. - function _addFunction(DiamondStorage storage _ds, bytes4 _selector, uint96 _selectorPosition, address _facetAddress) - internal - { + function _addFunction( + DiamondStorage storage _ds, + bytes4 _selector, + uint96 _selectorPosition, + address _facetAddress + ) internal { _ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition; _ds.facetToSelectorsAndPosition[_facetAddress].functionSelectors.push(_selector); _ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress; @@ -145,13 +238,13 @@ library LibDiamond { // an immutable function is a function defined directly in a diamond if (_facetAddress == address(this)) revert CannotRemoveImmutableFunction(_selector); // replace selector with last selector, then delete last selector - uint256 selectorPosition = _ds.selectorToFacetAndPosition[_selector].functionSelectorPosition; - uint256 lastSelectorPosition = _ds.facetToSelectorsAndPosition[_facetAddress].functionSelectors.length - 1; + uint96 selectorPosition = _selectorToPosition(_ds, _selector); + uint256 lastSelectorPosition = _facetToSelectors(_ds, _facetAddress).length - 1; // if not the same then replace _selector with lastSelector if (selectorPosition != lastSelectorPosition) { - bytes4 lastSelector = _ds.facetToSelectorsAndPosition[_facetAddress].functionSelectors[lastSelectorPosition]; + bytes4 lastSelector = _facetToSelectors(_ds, _facetAddress)[lastSelectorPosition]; _ds.facetToSelectorsAndPosition[_facetAddress].functionSelectors[selectorPosition] = lastSelector; - _ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition); + _ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = selectorPosition; } // delete the last selector _ds.facetToSelectorsAndPosition[_facetAddress].functionSelectors.pop(); @@ -161,7 +254,7 @@ library LibDiamond { if (lastSelectorPosition == 0) { // replace facet address with last facet address and delete last facet address uint256 lastFacetAddressPosition = _ds.facetAddresses.length - 1; - uint256 facetAddressPosition = _ds.facetToSelectorsAndPosition[_facetAddress].facetAddressPosition; + uint256 facetAddressPosition = _facetToPosition(_ds, _facetAddress); if (facetAddressPosition != lastFacetAddressPosition) { address lastFacetAddress = _ds.facetAddresses[lastFacetAddressPosition]; _ds.facetAddresses[facetAddressPosition] = lastFacetAddress; @@ -172,6 +265,47 @@ library LibDiamond { } } + //*////////////////////////////////////////////////////////////////////////// + // VIEW FUNCTIONS + //////////////////////////////////////////////////////////////////////////*// + + function _selectorToFacet(bytes4 _selector) internal view returns (address facet_) { + facet_ = _selectorToFacet(_diamondStorage(), _selector); + if (facet_ == address(0)) revert FunctionDoesNotExist(msg.sig); + } + + function _selectorToPosition(bytes4 _selector) internal view returns (uint96) { + return _selectorToPosition(_diamondStorage(), _selector); + } + + function _facetToSelectors(address _facetAddress) internal view returns (bytes4[] memory) { + return _facetToSelectors(_diamondStorage(), _facetAddress); + } + + function _facetToPosition(address _facetAddress) internal view returns (uint256) { + return _facetToPosition(_diamondStorage(), _facetAddress); + } + + function _selectorToFacet(DiamondStorage storage _ds, bytes4 _selector) internal view returns (address) { + return _ds.selectorToFacetAndPosition[_selector].facetAddress; + } + + function _selectorToPosition(DiamondStorage storage _ds, bytes4 _selector) internal view returns (uint96) { + return _ds.selectorToFacetAndPosition[_selector].functionSelectorPosition; + } + + function _facetToSelectors(DiamondStorage storage _ds, address _facetAddress) + internal + view + returns (bytes4[] memory) + { + return _ds.facetToSelectorsAndPosition[_facetAddress].functionSelectors; + } + + function _facetToPosition(DiamondStorage storage _ds, address _facetAddress) internal view returns (uint256) { + return _ds.facetToSelectorsAndPosition[_facetAddress].facetAddressPosition; + } + //*////////////////////////////////////////////////////////////////////////// // DIAMOND INITIALIZER //////////////////////////////////////////////////////////////////////////*// @@ -180,9 +314,28 @@ library LibDiamond { /// @param _init The address of the contract or facet to execute `data`. /// @param _calldata A function call, including function selector and arguments. function _initializeDiamondCut(address _init, bytes memory _calldata) internal { - if (_init == address(0)) { - return; + if (_init == address(0)) return; + _enforceHasContractCode(_init); + (bool success, bytes memory err) = _init.delegatecall(_calldata); + if (!success) { + if (err.length > 0) { + // bubble up error + /// @solidity memory-safe-assembly + assembly { + let returndata_size := mload(err) + revert(add(32, err), returndata_size) + } + } else { + revert InitializationFunctionReverted(_init, _calldata); + } } + } + + /// @dev Initialize the diamond cut. + /// @param _init The address of the contract or facet to execute `data`. + /// @param _calldata A function call, including function selector and arguments. + function _initializeDiamondCutCalldata(address _init, bytes calldata _calldata) internal { + if (_init == address(0)) return; _enforceHasContractCode(_init); (bool success, bytes memory err) = _init.delegatecall(_calldata); if (!success) { diff --git a/src/libraries/LibOwnableRoles.sol b/src/libraries/LibOwnableRoles.sol index b54e1ce..5d3c4bd 100644 --- a/src/libraries/LibOwnableRoles.sol +++ b/src/libraries/LibOwnableRoles.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; /// @title LibOwnableRoles /// @notice Simple single owner and multiroles authorization mixin. -/// @author David Dada -/// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol) +/// @author Vectorized (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol) +/// @author Modified by David Dada (https://github.com/dadadave80) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. @@ -102,8 +102,8 @@ library LibOwnableRoles { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. - function _guardInitializeOwner() internal pure returns (bool guard_) { - guard_ = true; + function _guardInitializeOwner() internal pure returns (bool) { + return true; } /// @dev Initializes the owner directly without authorization guard. diff --git a/src/libraries/errors/DiamondErrors.sol b/src/libraries/errors/DiamondErrors.sol index f401fc5..71aafa8 100644 --- a/src/libraries/errors/DiamondErrors.sol +++ b/src/libraries/errors/DiamondErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; //*////////////////////////////////////////////////////////////////////////// // DIAMOND LIBRARY ERRORS @@ -23,10 +23,6 @@ error CannotAddSelectorsToZeroAddress(bytes4[] selectors); /// @param contractAddress The address checked for deployed bytecode error NoBytecodeAtAddress(address contractAddress); -/// @notice Thrown when an unrecognized action is passed to the diamond cut -/// @param action The raw uint8 action value provided -error IncorrectFacetCutAction(uint8 action); - /// @notice Thrown when adding a function selector that already exists in the diamond /// @param selector The selector that is already present error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 selector); diff --git a/src/libraries/logs/DiamondLogs.sol b/src/libraries/logs/DiamondLogs.sol index 5fabf61..9cec4b1 100644 --- a/src/libraries/logs/DiamondLogs.sol +++ b/src/libraries/logs/DiamondLogs.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; import {FacetCut} from "@diamond-storage/DiamondStorage.sol"; diff --git a/src/libraries/storage/DiamondStorage.sol b/src/libraries/storage/DiamondStorage.sol index 9779d87..8173eed 100644 --- a/src/libraries/storage/DiamondStorage.sol +++ b/src/libraries/storage/DiamondStorage.sol @@ -1,29 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -//*////////////////////////////////////////////////////////////////////////// -// DIAMOND STORAGE TYPES -//////////////////////////////////////////////////////////////////////////*// - -/// @dev This struct is used to store the facet address and position of the -/// function selector in the facetToSelectorsAndPosition.functionSelectors -/// array. -struct FacetAddressAndPosition { - address facetAddress; - uint96 functionSelectorPosition; -} - -/// @dev This struct is used to store the function selectors and position of -/// the facet address in the facetAddresses array. -struct FacetFunctionSelectorsAndPosition { - bytes4[] functionSelectors; - uint256 facetAddressPosition; -} +pragma solidity ^0.8.20; //*////////////////////////////////////////////////////////////////////////// // DIAMOND STORAGE //////////////////////////////////////////////////////////////////////////*// +// keccak256(abi.encode(uint256(keccak256("diamond.standard.diamond.storage")) - 1)) & ~bytes32(uint256(0xff)); +bytes32 constant DIAMOND_STORAGE_LOCATION = 0x44fefae66705534388ac21ba5f0775616856a675b8eaea9bb0b2507f06238700; + /// @notice Storage structure for managing facets and interface support in a Diamond (EIP-2535) proxy /// @dev Tracks function selector mappings, facet lists, and ERC-165 interface support /// @custom:storage-location erc7201:diamond.standard.diamond.storage @@ -38,8 +22,24 @@ struct DiamondStorage { mapping(bytes4 => bool) supportedInterfaces; } -// keccak256(abi.encode(uint256(keccak256("diamond.standard.diamond.storage")) - 1)) & ~bytes32(uint256(0xff)); -bytes32 constant DIAMOND_STORAGE_LOCATION = 0x44fefae66705534388ac21ba5f0775616856a675b8eaea9bb0b2507f06238700; +//*////////////////////////////////////////////////////////////////////////// +// DIAMOND STORAGE TYPES +//////////////////////////////////////////////////////////////////////////*// + +/// @dev This struct is used to store the facet address and position of the +/// function selector in the facetToSelectorsAndPosition.functionSelectors +/// array. +struct FacetAddressAndPosition { + address facetAddress; + uint96 functionSelectorPosition; +} + +/// @dev This struct is used to store the function selectors and position of +/// the facet address in the facetAddresses array. +struct FacetFunctionSelectorsAndPosition { + bytes4[] functionSelectors; + uint256 facetAddressPosition; +} //*////////////////////////////////////////////////////////////////////////// // DIAMOND CUT TYPES @@ -64,16 +64,6 @@ struct FacetCut { bytes4[] functionSelectors; } -/// @notice Initialization parameters passed to the Diamond constructor -/// @param owner The address that will be granted the initial ownership/roles -/// @param init Optional address of a contract to call after the cut (use zero address for none) -/// @param initData Calldata to pass to the init contract (if any) -struct DiamondArgs { - address owner; - address init; - bytes initData; -} - /// @notice Struct representing a facet in a diamond contract. /// @dev Used for introspection of facet data through loupe functions. /// @param facetAddress The address of the facet contract. diff --git a/test/DiamondTester.t.sol b/test/DiamondTester.t.sol index ff852f6..d09fe8c 100644 --- a/test/DiamondTester.t.sol +++ b/test/DiamondTester.t.sol @@ -1,9 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; import {Facet} from "@diamond-storage/DiamondStorage.sol"; -import {DeployedDiamondState} from "@diamond-test/states/DeployedDiamondState.sol"; import {Utils} from "@diamond-test/helpers/Utils.sol"; +import {DeployedDiamondState} from "@diamond-test/states/DeployedDiamondState.sol"; +import {IDiamondCut} from "@diamond/interfaces/IDiamondCut.sol"; +import {IDiamondLoupe} from "@diamond/interfaces/IDiamondLoupe.sol"; /// @title DiamondTester /// @notice Contains test cases to validate the deployment and structure of the Diamond contract. @@ -30,7 +32,7 @@ contract DiamondTester is DeployedDiamondState { /// @notice Ensures all function selectors are registered correctly for each facet. /// @dev Compares generated selectors with those registered in the diamond via facetAddress(). - function testSelectorsAreComplete() public { + function testSelectorsAreComplete() public view { for (uint256 i; i < facetAddresses.length; ++i) { bytes4[] memory fromGenSelectors = _getSelectors(facetNames[i]); for (uint256 j; j < fromGenSelectors.length; ++j) { @@ -83,11 +85,11 @@ contract DiamondTester is DeployedDiamondState { /// @notice Confirms IDiamondCut interface support. function testSupportsIDiamondCut() public view { - assertTrue(diamondLoupe.supportsInterface(0x1f931c1c)); // IDiamondCut interface ID + assertTrue(diamondLoupe.supportsInterface(type(IDiamondCut).interfaceId)); // IDiamondCut interface ID } /// @notice Confirms IDiamondLoupe interface support. function testSupportsIDiamondLoupe() public view { - assertTrue(diamondLoupe.supportsInterface(0x48e2b093)); // IDiamondLoupe interface ID + assertTrue(diamondLoupe.supportsInterface(type(IDiamondLoupe).interfaceId)); // IDiamondLoupe interface ID } } diff --git a/test/helpers/GetSelectors.sol b/test/helpers/GetSelectors.sol index 9ffdc5b..9f5bc6d 100644 --- a/test/helpers/GetSelectors.sol +++ b/test/helpers/GetSelectors.sol @@ -8,24 +8,17 @@ abstract contract GetSelectors is Test { /// @dev Uses `vm.ffi` to execute a shell command that retrieves method identifiers. /// @param _facet The name of the facet contract to inspect. /// @return selectors_ An array of function selectors extracted from the facet. - function _getSelectors(string memory _facet) internal returns (bytes4[] memory selectors_) { - string[] memory cmd = new string[](5); - cmd[0] = "forge"; - cmd[1] = "inspect"; - cmd[2] = _facet; - cmd[3] = "methodIdentifiers"; - cmd[4] = "--json"; + function _getSelectors(string memory _facet) internal view returns (bytes4[] memory selectors_) { + string memory path = string.concat("out/", _facet, ".sol/", _facet, ".json"); + // forge-lint: disable-next-line(unsafe-cheatcode) + string memory artifact = vm.readFile(path); - bytes memory res = vm.ffi(cmd); - string memory output = string(res); - - string[] memory keys = vm.parseJsonKeys(output, ""); + string[] memory keys = vm.parseJsonKeys(artifact, "$.methodIdentifiers"); uint256 keysLength = keys.length; - selectors_ = new bytes4[](keysLength); for (uint256 i; i < keysLength; ++i) { - selectors_[i] = bytes4(bytes32(keccak256(bytes(keys[i])))); + selectors_[i] = bytes4(keccak256(bytes(keys[i]))); } } } diff --git a/test/helpers/Utils.sol b/test/helpers/Utils.sol index 4e54449..5980a13 100644 --- a/test/helpers/Utils.sol +++ b/test/helpers/Utils.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; -import {IDiamondLoupe} from "@diamond/interfaces/IDiamondLoupe.sol"; import {Facet} from "@diamond-storage/DiamondStorage.sol"; +import {IDiamondLoupe} from "@diamond/interfaces/IDiamondLoupe.sol"; /// @notice A utility contract providing helper functions for working with diamond facets and selectors. /// @author David Dada diff --git a/test/mocks/MockDiamond.sol b/test/mocks/MockDiamond.sol new file mode 100644 index 0000000..72cc358 --- /dev/null +++ b/test/mocks/MockDiamond.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Diamond, FacetCut} from "@diamond/Diamond.sol"; + +contract MockDiamond is Diamond { + constructor(FacetCut[] memory _facetCuts, address _init, bytes memory _calldata) + payable + Diamond(_facetCuts, _init, _calldata) + {} + + function _beforeDelegate() internal override { + // Add custom logic before delegatecall here + } +} diff --git a/test/states/DeployedDiamondState.sol b/test/states/DeployedDiamondState.sol index 0d46b28..aad8677 100644 --- a/test/states/DeployedDiamondState.sol +++ b/test/states/DeployedDiamondState.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; +pragma solidity ^0.8.20; -import {GetSelectors} from "@diamond-test/helpers/GetSelectors.sol"; import {DeployDiamond} from "@diamond-script/DeployDiamond.s.sol"; -import {DiamondCutFacet} from "@diamond/facets/DiamondCutFacet.sol"; +import {GetSelectors} from "@diamond-test/helpers/GetSelectors.sol"; import {DiamondLoupeFacet} from "@diamond/facets/DiamondLoupeFacet.sol"; import {OwnableRolesFacet} from "@diamond/facets/OwnableRolesFacet.sol"; +import {IDiamondCut} from "@diamond/interfaces/IDiamondCut.sol"; /// @notice Provides shared state for tests involving a freshly deployed Diamond contract. /// @dev Sets up references to deployed facets, interfaces, and the diamond itself for testing. @@ -15,7 +15,7 @@ abstract contract DeployedDiamondState is GetSelectors { address public diamond; /// @notice Interface for the DiamondCut functionality of the deployed diamond. - DiamondCutFacet public diamondCut; + IDiamondCut public diamondCut; /// @notice Interface for the DiamondLoupe functionality of the deployed diamond. DiamondLoupeFacet public diamondLoupe; @@ -29,7 +29,7 @@ abstract contract DeployedDiamondState is GetSelectors { /// @notice List of facet contract names used in deployment. string[3] public facetNames = ["DiamondCutFacet", "DiamondLoupeFacet", "OwnableRolesFacet"]; - // address public diamondOwner = makeAddr("Owner"); + address public diamondOwner = address(this); /// @notice Deploys the Diamond contract and initializes interface references and facet addresses. /// @dev This function is intended to be called in a test setup phase (e.g., `setUp()` in Foundry). @@ -37,7 +37,7 @@ abstract contract DeployedDiamondState is GetSelectors { deployDiamond = new DeployDiamond(); diamond = deployDiamond.run(); - diamondCut = DiamondCutFacet(diamond); + diamondCut = IDiamondCut(diamond); diamondLoupe = DiamondLoupeFacet(diamond); ownableRoles = OwnableRolesFacet(diamond);