Skip to content

Commit dd922fc

Browse files
authored
Merge pull request #22 from dadadave80:chore/replace-ownable-roles-with-ownable
Refactor and enhance Diamond library with initializable pattern
2 parents 3046014 + 1dbc25a commit dd922fc

14 files changed

Lines changed: 157 additions & 619 deletions

script/DeployDiamond.s.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {GetSelectors} from "@diamond-test/helpers/GetSelectors.sol";
55
import {MockDiamond} from "@diamond-test/mocks/MockDiamond.sol";
66
import {DiamondCutFacet} from "@diamond/facets/DiamondCutFacet.sol";
77
import {DiamondLoupeFacet} from "@diamond/facets/DiamondLoupeFacet.sol";
8-
import {OwnableRolesFacet} from "@diamond/facets/OwnableRolesFacet.sol";
8+
import {OwnableFacet} from "@diamond/facets/OwnableFacet.sol";
99
import {ERC165Init} from "@diamond/initializers/ERC165Init.sol";
1010
import {MultiInit} from "@diamond/initializers/MultiInit.sol";
1111
import {OwnableInit} from "@diamond/initializers/OwnableInit.sol";
@@ -17,7 +17,7 @@ import {Script} from "forge-std/Script.sol";
1717
/// @notice Deployment script for an EIP-2535 Diamond proxy contract with core facets and ERC165 initialization
1818
/// @author David Dada
1919
///
20-
/// @dev Uses Foundry's `Script` and a helper contract to deploy and wire up DiamondCutFacet, DiamondLoupeFacet, and OwnableRolesFacet
20+
/// @dev Uses Foundry's `Script` and a helper contract to deploy and wire up DiamondCutFacet, DiamondLoupeFacet, and OwnableFacet
2121
contract DeployDiamond is Script, GetSelectors {
2222
/// @notice Executes the deployment of the Diamond contract with the initial facets and ERC165 interface setup
2323
/// @dev Broadcasts transactions using Foundry's scripting environment (`vm.startBroadcast()` and `vm.stopBroadcast()`).
@@ -28,7 +28,7 @@ contract DeployDiamond is Script, GetSelectors {
2828
// Deploy core facet contracts
2929
DiamondCutFacet diamondCutFacet = new DiamondCutFacet();
3030
DiamondLoupeFacet diamondLoupeFacet = new DiamondLoupeFacet();
31-
OwnableRolesFacet ownableRolesFacet = new OwnableRolesFacet();
31+
OwnableFacet ownableFacet = new OwnableFacet();
3232

3333
// Deploy initializer contracts
3434
address multiInit = address(new MultiInit());
@@ -52,11 +52,11 @@ contract DeployDiamond is Script, GetSelectors {
5252
functionSelectors: _getSelectors("DiamondLoupeFacet")
5353
});
5454

55-
// Add OwnableRolesFacet to the cut list
55+
// Add OwnableFacet to the cut list
5656
cut[2] = FacetCut({
57-
facetAddress: address(ownableRolesFacet),
57+
facetAddress: address(ownableFacet),
5858
action: FacetCutAction.Add,
59-
functionSelectors: _getSelectors("OwnableRolesFacet")
59+
functionSelectors: _getSelectors("OwnableFacet")
6060
});
6161

6262
// Build MultiInit arrays for granular initialization

src/Diamond.sol

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,26 +55,13 @@ abstract contract Diamond {
5555
/// @notice Fallback function that delegates calls to the appropriate facet based on function selector
5656
/// @dev Reads the facet address from diamond storage and performs a delegatecall; reverts if selector is not found
5757
fallback() external payable virtual {
58-
_delegate(_facet());
59-
}
60-
61-
receive() external payable virtual {}
62-
63-
/// @notice Retrieves the implementation address for the current function call
64-
/// @dev A Facet is one of many implementations in a Diamond Proxy
65-
function _facet() internal view virtual returns (address) {
66-
return DiamondLib.selectorToFacet(msg.sig);
67-
}
68-
69-
/// @notice Internal function to perform a delegatecall to an implementation
70-
/// @param _implementation Address of the implementation to delegate to
71-
function _delegate(address _implementation) internal virtual {
58+
address implementation = DiamondLib.selectorToFacet(msg.sig);
7259
assembly {
7360
// Copy calldata to memory
7461
calldatacopy(0, 0, calldatasize())
7562

7663
// Delegate call to implementation
77-
let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0)
64+
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
7865

7966
// Copy returned data
8067
returndatacopy(0, 0, returndatasize())
@@ -89,4 +76,6 @@ abstract contract Diamond {
8976
}
9077
}
9178
}
79+
80+
receive() external payable virtual {}
9281
}

src/facets/DiamondCutFacet.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pragma solidity ^0.8.20;
33

44
import {IDiamondCut} from "@diamond/interfaces/IDiamondCut.sol";
55
import {DiamondLib, FacetCut} from "@diamond/libraries/DiamondLib.sol";
6-
import {OwnableRolesLib} from "@diamond/libraries/OwnableRolesLib.sol";
6+
import {OwnableLib} from "@diamond/libraries/OwnableLib.sol";
77

88
/// @title DiamondCutFacet
99
/// @notice Simple single owner and multiroles authorization mixin.
@@ -22,7 +22,7 @@ contract DiamondCutFacet is IDiamondCut {
2222
/// _calldata is executed with delegatecall on _init
2323
function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external payable {
2424
// Check that the caller is the owner
25-
OwnableRolesLib.checkOwner();
25+
OwnableLib.checkOwner();
2626
// Call the diamond cut function from the library
2727
DiamondLib.diamondCut(_diamondCut, _init, _calldata);
2828
}

src/facets/OwnableFacet.sol

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
import {OwnableLib} from "@diamond/libraries/OwnableLib.sol";
5+
6+
/// @title OwnableRolesFacet
7+
/// @notice Simple single owner and multiroles authorization mixin.
8+
/// @author David Dada
9+
/// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol)
10+
///
11+
/// @dev Note:
12+
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
13+
/// You MUST call the `_initializeOwner` in the constructor / initializer.
14+
///
15+
/// While the ownable portion follows
16+
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
17+
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
18+
contract OwnableFacet {
19+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
20+
/* PUBLIC UPDATE FUNCTIONS */
21+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
22+
23+
/// @dev Allows the owner to transfer the ownership to `newOwner`.
24+
function transferOwnership(address _newOwner) public onlyOwner {
25+
OwnableLib.transferOwnership(_newOwner);
26+
}
27+
28+
/// @dev Allows the owner to renounce their ownership.
29+
function renounceOwnership() public onlyOwner {
30+
OwnableLib.renounceOwnership();
31+
}
32+
33+
/// @dev Request a two-step ownership handover to the caller.
34+
/// The request will automatically expire in 48 hours (172800 seconds) by default.
35+
function requestOwnershipHandover() public {
36+
OwnableLib.requestOwnershipHandover();
37+
}
38+
39+
/// @dev Cancels the two-step ownership handover to the caller, if any.
40+
function cancelOwnershipHandover() public {
41+
OwnableLib.cancelOwnershipHandover();
42+
}
43+
44+
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
45+
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
46+
function completeOwnershipHandover(address _pendingOwner) public onlyOwner {
47+
OwnableLib.completeOwnershipHandover(_pendingOwner);
48+
}
49+
50+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
51+
/* PUBLIC READ FUNCTIONS */
52+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
53+
54+
/// @dev Returns the owner of the contract.
55+
function owner() public view returns (address) {
56+
return OwnableLib.owner();
57+
}
58+
59+
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
60+
function ownershipHandoverExpiresAt(address _pendingOwner) public view returns (uint256) {
61+
return OwnableLib.ownershipHandoverExpiresAt(_pendingOwner);
62+
}
63+
64+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
65+
/* MODIFIERS */
66+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
67+
68+
/// @dev Marks a function as only callable by the owner.
69+
modifier onlyOwner() {
70+
OwnableLib.checkOwner();
71+
_;
72+
}
73+
}

src/facets/OwnableRolesFacet.sol

Lines changed: 0 additions & 142 deletions
This file was deleted.

src/initializers/DiamondInit.sol

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/initializers/MultiInit.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ error AddressAndCalldataLengthMismatch();
77
error NoBytecodeAtAddress(address initAddress);
88
error InitializeReverted(address initAddress, bytes initCalldata);
99

10+
/// @title MultiInit
11+
/// @author David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80)
12+
/// @notice Provides a utility function to perform multiple delegatecall initializations in a single transaction,
13+
/// with robust error handling and validation.
1014
contract MultiInit {
1115
function multiInit(address[] calldata _initAddresses, bytes[] calldata _initData) public {
1216
uint256 initAddressesLength = _initAddresses.length;
@@ -23,8 +27,8 @@ contract MultiInit {
2327
if (err.length > 0) {
2428
// bubble up error
2529
assembly ("memory-safe") {
26-
let returndata_size := mload(err)
27-
revert(add(32, err), returndata_size)
30+
let returndataSize := mload(err)
31+
revert(add(32, err), returndataSize)
2832
}
2933
} else {
3034
revert InitializeReverted(initAddress, _initData[i]);

src/initializers/OwnableInit.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.20;
33

4-
import {OwnableRolesLib} from "@diamond/libraries/OwnableRolesLib.sol";
4+
import {OwnableLib} from "@diamond/libraries/OwnableLib.sol";
55

66
/// @title OwnableInit
77
/// @notice Provides an initializer to set the contract owner
@@ -13,6 +13,6 @@ contract OwnableInit {
1313
/// @dev This function is called during the diamond cut process to set up the owner.
1414
/// @param _owner The address to set as the contract owner.
1515
function initOwner(address _owner) public {
16-
OwnableRolesLib.initializeOwner(_owner);
16+
OwnableLib.initializeOwner(_owner);
1717
}
1818
}

0 commit comments

Comments
 (0)