Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
DiamondTester:testDiamondDeployed() (gas: 5289)
DiamondTester:testDiamondOwner() (gas: 17516)
DiamondTester:testFacetAddressToSelectorsMappingIsCorrect() (gas: 140449)
DiamondTester:testSelectorToFacetMappingIsCorrect() (gas: 138419)
DiamondTester:testSelectorsAreComplete() (gas: 144484)
DiamondTester:testSelectorsAreUnique() (gas: 183338)
DiamondTester:testStandardFacetsDeployed() (gas: 13886)
DiamondTester:testSupportsERC165() (gas: 15452)
DiamondTester:testSupportsERC173() (gas: 15497)
DiamondTester:testDiamondDeployed() (gas: 5278)
DiamondTester:testDiamondOwner() (gas: 15392)
DiamondTester:testFacetAddressToSelectorsMappingIsCorrect() (gas: 139943)
DiamondTester:testSelectorToFacetMappingIsCorrect() (gas: 137976)
DiamondTester:testSelectorsAreComplete() (gas: 144581)
DiamondTester:testSelectorsAreUnique() (gas: 191268)
DiamondTester:testStandardFacetsDeployed() (gas: 13908)
DiamondTester:testSupportsERC165() (gas: 15496)
DiamondTester:testSupportsERC173() (gas: 15475)
DiamondTester:testSupportsIDiamondCut() (gas: 15465)
DiamondTester:testSupportsIDiamondLoupe() (gas: 15474)
DiamondTester:testSupportsIDiamondLoupe() (gas: 15496)
5 changes: 5 additions & 0 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"lib/forge-std": {
"rev": "3b20d60d14b343ee4f908cb8079495c07f5e8981"
}
}
7 changes: 1 addition & 6 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,5 @@ remappings = [
'@diamond-logs/=src/libraries/logs/',
'@diamond-errors/=src/libraries/errors/'
]
auto_detect_remappings = true
ffi = true
optimizer_runs = 20_000
evm_version = "prague"
allow_internal_expect_revert = false
names = true
sizes = true
ffi = true
56 changes: 52 additions & 4 deletions script/DeployDiamond.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,68 @@
pragma solidity ^0.8.4;

import {Script} from "forge-std/Script.sol";
import {DeployDiamondHelper} from "@diamond-test/helpers/DeployDiamondHelper.sol";
import {Diamond} from "@diamond/Diamond.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";

/// @title DeployDiamond
/// @notice Deployment script for an EIP-2535 Diamond proxy contract with core facets and ERC165 initialization
/// @author David Dada
///
/// @dev Uses Foundry's `Script` and a helper contract to deploy and wire up DiamondCutFacet, DiamondLoupeFacet, and OwnableRolesFacet
contract DeployDiamond is Script, DeployDiamondHelper {
contract DeployDiamond is Script, GetSelectors {
/// @notice Executes the deployment of the Diamond contract with the initial facets and ERC165 interface setup
/// @dev Broadcasts transactions using Foundry's scripting environment (`vm.startBroadcast()` and `vm.stopBroadcast()`).
/// Deploys three core facets, sets up DiamondArgs, encodes an initializer call, and constructs the Diamond.
/// @return diamond_ The address of the deployed Diamond proxy contract
function run() external returns (address diamond_) {
vm.broadcast();
diamond_ = _deployDiamond(msg.sender);
vm.startBroadcast();
// Deploy core facet contracts
DiamondCutFacet diamondCutFacet = new DiamondCutFacet();
DiamondLoupeFacet diamondLoupeFacet = new DiamondLoupeFacet();
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()")
});

// Create an array of FacetCut entries for standard facets
FacetCut[] memory cut = new FacetCut[](3);

// Add DiamondCutFacet to the cut list
cut[0] = FacetCut({
facetAddress: address(diamondCutFacet),
action: FacetCutAction.Add,
functionSelectors: _getSelectors("DiamondCutFacet")
});

// Add DiamondLoupeFacet to the cut list
cut[1] = FacetCut({
facetAddress: address(diamondLoupeFacet),
action: FacetCutAction.Add,
functionSelectors: _getSelectors("DiamondLoupeFacet")
});

// Add OwnableRolesFacet to the cut list
cut[2] = FacetCut({
facetAddress: address(ownableRolesFacet),
action: FacetCutAction.Add,
functionSelectors: _getSelectors("OwnableRolesFacet")
});

// Deploy the Diamond contract with the facets and initialization args
Diamond diamond = new Diamond(cut, args);
diamond_ = address(diamond);
vm.stopBroadcast();
}
}
1 change: 0 additions & 1 deletion src/libraries/LibDiamond.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import "@diamond-errors/DiamondErrors.sol";
/// @notice Internal library providing core functionality for EIP-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)
/// @author Modified from Timo (https://github.com/FydeTreasury/Diamond-Foundry/blob/main/src/libraries/LibDiamond.sol)
///
/// @dev Defines the diamond storage layout and implements the `_diamondCut` operation and storage accessors
library LibDiamond {
Expand Down
9 changes: 5 additions & 4 deletions test/DiamondTester.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
pragma solidity ^0.8.4;

import {Facet} from "@diamond-storage/DiamondStorage.sol";
import {DeployedDiamondState} from "@diamond-test/helpers/TestStates.sol";
import {DeployedDiamondState} from "@diamond-test/states/DeployedDiamondState.sol";
import {Utils} from "@diamond-test/helpers/Utils.sol";

/// @title DiamondTester
/// @notice Contains test cases to validate the deployment and structure of the Diamond contract.
Expand All @@ -15,7 +16,7 @@ contract DiamondTester is DeployedDiamondState {

/// @notice Verifies the diamond owner is set correctly.
function testDiamondOwner() public view {
assertEq(ownableRoles.owner(), diamondOwner);
assertEq(ownableRoles.owner(), address(this));
}

/// @notice Checks that the standard facets are deployed and have valid addresses.
Expand All @@ -31,7 +32,7 @@ contract DiamondTester is DeployedDiamondState {
/// @dev Compares generated selectors with those registered in the diamond via facetAddress().
function testSelectorsAreComplete() public {
for (uint256 i; i < facetAddresses.length; ++i) {
bytes4[] memory fromGenSelectors = _generateSelectors(facetNames[i]);
bytes4[] memory fromGenSelectors = _getSelectors(facetNames[i]);
for (uint256 j; j < fromGenSelectors.length; ++j) {
assertEq(facetAddresses[i], diamondLoupe.facetAddress(fromGenSelectors[j]));
}
Expand All @@ -40,7 +41,7 @@ contract DiamondTester is DeployedDiamondState {

/// @notice Asserts that all function selectors across all facets are unique.
function testSelectorsAreUnique() public view {
bytes4[] memory allSelectors = getAllSelectors(address(diamond));
bytes4[] memory allSelectors = Utils.getAllSelectors(address(diamond));
for (uint256 i; i < allSelectors.length; ++i) {
for (uint256 j = i + 1; j < allSelectors.length; ++j) {
assertNotEq(allSelectors[i], allSelectors[j]);
Expand Down
54 changes: 0 additions & 54 deletions test/helpers/DeployDiamondHelper.sol

This file was deleted.

31 changes: 31 additions & 0 deletions test/helpers/GetSelectors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";

abstract contract GetSelectors is Test {
/// @notice Generates function selectors for a given facet using Foundry's `forge inspect`.
/// @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";

bytes memory res = vm.ffi(cmd);
string memory output = string(res);

string[] memory keys = vm.parseJsonKeys(output, "");
uint256 keysLength = keys.length;

selectors_ = new bytes4[](keysLength);

for (uint256 i; i < keysLength; ++i) {
selectors_[i] = bytes4(bytes32(keccak256(bytes(keys[i]))));
}
}
}
29 changes: 1 addition & 28 deletions test/helpers/HelperContract.sol → test/helpers/Utils.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {Test} from "forge-std/Test.sol";
import {IDiamondLoupe} from "@diamond/interfaces/IDiamondLoupe.sol";
import {Facet} from "@diamond-storage/DiamondStorage.sol";

Expand All @@ -10,33 +9,7 @@ import {Facet} from "@diamond-storage/DiamondStorage.sol";
/// @author Modified from Timo (https://github.com/FydeTreasury/Diamond-Foundry/blob/main/test/HelperContract.sol)
///
/// @dev Includes support for generating selectors using Foundry FFI, array manipulation, and facet inspection.
abstract contract HelperContract is Test {
/// @notice Generates function selectors for a given facet using Foundry's `forge inspect`.
/// @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 _generateSelectors(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";

bytes memory res = vm.ffi(cmd);
string memory output = string(res);

string[] memory keys = vm.parseJsonKeys(output, "$");
uint256 keysLength = keys.length;

// Initialize the selectors array with the selectorCount
selectors_ = new bytes4[](keysLength);

for (uint256 i; i < keysLength; ++i) {
selectors_[i] = bytes4(bytes32(keccak256(bytes(keys[i]))));
}
}

library Utils {
/// @notice Removes an element from a bytes4 array at the given index.
/// @param _index The index of the element to remove.
/// @param _array The original array.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {DeployDiamondHelper} from "@diamond-test/helpers/DeployDiamondHelper.sol";
import {GetSelectors} from "@diamond-test/helpers/GetSelectors.sol";
import {DeployDiamond} from "@diamond-script/DeployDiamond.s.sol";
import {DiamondCutFacet} from "@diamond/facets/DiamondCutFacet.sol";
import {DiamondLoupeFacet} from "@diamond/facets/DiamondLoupeFacet.sol";
import {OwnableRolesFacet} from "@diamond/facets/OwnableRolesFacet.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.
abstract contract DeployedDiamondState is DeployDiamondHelper {
abstract contract DeployedDiamondState is GetSelectors {
DeployDiamond deployDiamond;
/// @notice Instance of the deployed Diamond contract.
address public diamond;

Expand All @@ -27,17 +29,17 @@ abstract contract DeployedDiamondState is DeployDiamondHelper {
/// @notice List of facet contract names used in deployment.
string[3] public facetNames = ["DiamondCutFacet", "DiamondLoupeFacet", "OwnableRolesFacet"];

address public diamondOwner = makeAddr("Owner");
// address public diamondOwner = makeAddr("Owner");

/// @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).
function setUp() public {
vm.startPrank(diamondOwner);
diamond = _deployDiamond(diamondOwner);
deployDiamond = new DeployDiamond();
diamond = deployDiamond.run();

diamondCut = DiamondCutFacet(address(diamond));
diamondLoupe = DiamondLoupeFacet(address(diamond));
ownableRoles = OwnableRolesFacet(address(diamond));
diamondCut = DiamondCutFacet(diamond);
diamondLoupe = DiamondLoupeFacet(diamond);
ownableRoles = OwnableRolesFacet(diamond);

facetAddresses = diamondLoupe.facetAddresses();
}
Expand Down