-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: diamond erc20 with Nayms governance and access control systems …
…[wip]
- Loading branch information
1 parent
c0e6781
commit aa97324
Showing
20 changed files
with
1,631 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"editor.formatOnSave": true, | ||
"solidity.formatter": "forge", | ||
"[solidity]": { | ||
"editor.defaultFormatter": "NomicFoundation.hardhat-solidity" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
forge-std/=lib/forge-std/src/ | ||
openzeppelin/=lib/openzeppelin-contracts/contracts/ | ||
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ | ||
diamond-2-hardhat/=lib/diamond-2-hardhat/contracts/ | ||
ds-test/=lib/forge-std/lib/ds-test/src/ | ||
erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/ | ||
openzeppelin-contracts/=lib/openzeppelin-contracts/ | ||
solady/=lib/solady/src/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.20; | ||
|
||
import { LibACL, LibHelpers } from "../libs/LibACL.sol"; | ||
import { LibConstants as LC } from "../libs/LibConstants.sol"; | ||
import { Modifiers } from "../shared/Modifiers.sol"; | ||
import { AssignerCannotUnassignRole } from "../shared/CustomErrors.sol"; | ||
|
||
/** | ||
* @title Access Control List | ||
* @notice Use it to authorize various actions on the contracts | ||
* @dev Use it to (un)assign or check role membership | ||
*/ | ||
contract ACLFacet is Modifiers { | ||
using LibHelpers for *; | ||
|
||
/** | ||
* @notice Assign a `_roleId` to the object in given context | ||
* @dev Any object ID can be a context, system is a special context with highest priority | ||
* @param _objectId ID of an object that is being assigned a role | ||
* @param _contextId ID of the context in which a role is being assigned | ||
* @param _role Name of the role being assigned | ||
*/ | ||
function assignRole(bytes32 _objectId, bytes32 _contextId, string memory _role) external { | ||
bytes32 assignerId = LibHelpers._getIdForAddress(msg.sender); | ||
require( | ||
LibACL._canAssign(assignerId, _objectId, _contextId, LibHelpers._stringToBytes32(_role)), | ||
"not in assigners group" | ||
); | ||
|
||
/// @dev First, assigner attempts to unassign the role. | ||
bytes32 roleId = LibACL._getRoleInContext(_objectId, _contextId); | ||
if (roleId != 0 && !LibACL._canAssign(assignerId, _objectId, _contextId, roleId)) { | ||
revert AssignerCannotUnassignRole(assignerId, _objectId, _contextId, string(roleId._bytes32ToBytes())); | ||
} | ||
LibACL._unassignRole(_objectId, _contextId); | ||
|
||
/// @dev Second, assign the role. | ||
LibACL._assignRole(_objectId, _contextId, LibHelpers._stringToBytes32(_role)); | ||
} | ||
|
||
/** | ||
* @notice Unassign object from a role in given context | ||
* @dev Any object ID can be a context, system is a special context with highest priority | ||
* @param _objectId ID of an object that is being unassigned from a role | ||
* @param _contextId ID of the context in which a role membership is being revoked | ||
*/ | ||
function unassignRole(bytes32 _objectId, bytes32 _contextId) external { | ||
bytes32 roleId = LibACL._getRoleInContext(_objectId, _contextId); | ||
bytes32 assignerId = LibHelpers._getIdForAddress(msg.sender); | ||
require(LibACL._canAssign(assignerId, _objectId, _contextId, roleId), "not in assigners group"); | ||
LibACL._unassignRole(_objectId, _contextId); | ||
} | ||
|
||
/** | ||
* @notice Checks if an object belongs to `_group` group in given context | ||
* @dev Assigning a role to the object makes it a member of a corresponding role group | ||
* @param _objectId ID of an object that is being checked for role group membership | ||
* @param _contextId Context in which membership should be checked | ||
* @param _group name of the role group | ||
* @return true if object with given ID is a member, false otherwise | ||
*/ | ||
function isInGroup(bytes32 _objectId, bytes32 _contextId, string memory _group) external view returns (bool) { | ||
return LibACL._isInGroup(_objectId, _contextId, LibHelpers._stringToBytes32(_group)); | ||
} | ||
|
||
/** | ||
* @notice Check whether a parent object belongs to the `_group` group in given context | ||
* @dev Objects can have a parent object, i.e. entity is a parent of a user | ||
* @param _objectId ID of an object whose parent is being checked for role group membership | ||
* @param _contextId Context in which the role group membership is being checked | ||
* @param _group name of the role group | ||
* @return true if object's parent is a member of this role group, false otherwise | ||
*/ | ||
function isParentInGroup( | ||
bytes32 _objectId, | ||
bytes32 _contextId, | ||
string memory _group | ||
) | ||
external | ||
view | ||
returns (bool) | ||
{ | ||
return LibACL._isParentInGroup(_objectId, _contextId, LibHelpers._stringToBytes32(_group)); | ||
} | ||
|
||
/** | ||
* @notice Check whether a user can assign specific object to the `_role` role in given context | ||
* @dev Check permission to assign to a role | ||
* @param _assignerId The object ID of the user who is assigning a role to another object. | ||
* @param _objectId ID of an object that is being checked for assigning rights | ||
* @param _contextId ID of the context in which permission is checked | ||
* @param _role name of the role to check | ||
* @return true if user has the right to assign, false otherwise | ||
*/ | ||
function canAssign( | ||
bytes32 _assignerId, | ||
bytes32 _objectId, | ||
bytes32 _contextId, | ||
string memory _role | ||
) | ||
external | ||
view | ||
returns (bool) | ||
{ | ||
return LibACL._canAssign(_assignerId, _objectId, _contextId, LibHelpers._stringToBytes32(_role)); | ||
} | ||
|
||
/** | ||
* @notice Check whether a user can call a specific function. | ||
* @param _userId The object ID of the user who is calling the function. | ||
* @param _contextId ID of the context in which permission is checked. | ||
* @param _groupId ID of the group in which permission is checked. | ||
*/ | ||
function hasGroupPrivilege(bytes32 _userId, bytes32 _contextId, bytes32 _groupId) external view returns (bool) { | ||
return LibACL._hasGroupPrivilege(_userId, _contextId, _groupId); | ||
} | ||
|
||
/** | ||
* @notice Get a user's (an objectId's) assigned role in a specific context | ||
* @param objectId ID of an object that is being checked for its assigned role in a specific context | ||
* @param contextId ID of the context in which the objectId's role is being checked | ||
* @return roleId objectId's role in the contextId | ||
*/ | ||
function getRoleInContext(bytes32 objectId, bytes32 contextId) external view returns (bytes32) { | ||
return LibACL._getRoleInContext(objectId, contextId); | ||
} | ||
|
||
/** | ||
* @notice Get whether role is in group. | ||
* @dev Get whether role is in group. | ||
* @param role the role. | ||
* @param group the group. | ||
* @return true if role is in group, false otherwise. | ||
*/ | ||
function isRoleInGroup(string memory role, string memory group) external view returns (bool) { | ||
return LibACL._isRoleInGroup(role, group); | ||
} | ||
|
||
/** | ||
* @notice Get whether given group can assign given role. | ||
* @dev Get whether given group can assign given role. | ||
* @param role the role. | ||
* @param group the group. | ||
* @return true if role can be assigned by group, false otherwise. | ||
*/ | ||
function canGroupAssignRole(string memory role, string memory group) external view returns (bool) { | ||
return LibACL._canGroupAssignRole(role, group); | ||
} | ||
|
||
/** | ||
* @notice Update who can assign `_role` role | ||
* @dev Update who has permission to assign this role | ||
* @param _role name of the role | ||
* @param _assignerGroup Group who can assign members to this role | ||
*/ | ||
function updateRoleAssigner( | ||
string memory _role, | ||
string memory _assignerGroup | ||
) | ||
external | ||
assertPrivilege(LC.SYSTEM_IDENTIFIER_BYTES32, LC.GROUP_SYSTEM_ADMINS) | ||
{ | ||
LibACL._updateRoleAssigner(_role, _assignerGroup); | ||
} | ||
|
||
/** | ||
* @notice Update role group membership for `_role` role and `_group` group | ||
* @dev Update role group membership | ||
* @param _role name of the role | ||
* @param _group name of the group | ||
* @param _roleInGroup is member of | ||
*/ | ||
function updateRoleGroup( | ||
string memory _role, | ||
string memory _group, | ||
bool _roleInGroup | ||
) | ||
external | ||
assertPrivilege(LC.SYSTEM_IDENTIFIER_BYTES32, LC.GROUP_SYSTEM_ADMINS) | ||
{ | ||
require(!strEquals(_group, LC.GROUP_SYSTEM_ADMINS), "system admins group is not modifiable"); | ||
LibACL._updateRoleGroup(_role, _group, _roleInGroup); | ||
} | ||
|
||
/** | ||
* @notice Compare two strings | ||
* @dev compares keccak256 hashes of ABI encoded strings | ||
* @param s1 first string to compare | ||
* @param s2 second string to compare | ||
* @return true is strings are equal | ||
*/ | ||
function strEquals(string memory s1, string memory s2) private pure returns (bool) { | ||
return keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.20; | ||
|
||
import { IDiamondCut } from "lib/diamond-2-hardhat/contracts/interfaces/IDiamondCut.sol"; | ||
import { Modifiers } from "../shared/Modifiers.sol"; | ||
import { AppStorage, LibAppStorage } from "../shared/AppStorage.sol"; | ||
import { LibGovernance } from "src/libs/LibGovernance.sol"; | ||
import { LibConstants as LC } from "src/libs/LibConstants.sol"; | ||
|
||
contract GovernanceFacet is Modifiers { | ||
event CreateUpgrade(bytes32 id, address indexed who); | ||
event UpdateUpgradeExpiration(uint256 duration); | ||
event UpgradeCancelled(bytes32 id, address indexed who); | ||
|
||
/** | ||
* @notice Check if the diamond has been initialized. | ||
* @dev This will get the value from AppStorage.diamondInitialized. | ||
*/ | ||
function isDiamondInitialized() external view returns (bool) { | ||
AppStorage storage s = LibAppStorage.diamondStorage(); | ||
return s.diamondInitialized; | ||
} | ||
|
||
/** | ||
* @notice Calcuate upgrade hash: `id` | ||
* @dev calucate the upgrade hash by hashing all the inputs | ||
* @param _diamondCut the array of FacetCut struct, IDiamondCut.FacetCut[] to be used for upgrade | ||
* @param _init address of the init diamond to be used for upgrade | ||
* @param _calldata bytes to be passed as call data for upgrade | ||
*/ | ||
function calculateUpgradeId( | ||
IDiamondCut.FacetCut[] calldata _diamondCut, | ||
address _init, | ||
bytes calldata _calldata | ||
) | ||
external | ||
pure | ||
returns (bytes32) | ||
{ | ||
return LibGovernance._calculateUpgradeId(_diamondCut, _init, _calldata); | ||
} | ||
|
||
/** | ||
* @notice Approve the following upgrade hash: `id` | ||
* @dev The diamondCut() has been modified to check if the upgrade has been scheduled. This method needs to be | ||
* called in order | ||
* for an upgrade to be executed. | ||
* @param id This is the keccak256(abi.encode(cut)), where cut is the array of FacetCut struct, | ||
* IDiamondCut.FacetCut[]. | ||
*/ | ||
function createUpgrade(bytes32 id) external assertPrivilege(LC.SYSTEM_IDENTIFIER_BYTES32, LC.GROUP_SYSTEM_ADMINS) { | ||
AppStorage storage s = LibAppStorage.diamondStorage(); | ||
|
||
if (s.upgradeScheduled[id] > block.timestamp) { | ||
revert("Upgrade has already been scheduled"); | ||
} | ||
// 0 == upgrade is not scheduled / has been cancelled | ||
// block.timestamp + upgradeExpiration == upgrade is scheduled and expires at this time | ||
// Set back to 0 when an upgrade is complete | ||
s.upgradeScheduled[id] = block.timestamp + s.upgradeExpiration; | ||
emit CreateUpgrade(id, msg.sender); | ||
} | ||
|
||
/** | ||
* @notice Update the diamond cut upgrade expiration period. | ||
* @dev When createUpgrade() is called, it allows a diamondCut() upgrade to be executed. This upgrade must be | ||
* executed before the | ||
* upgrade expires. The upgrade expires based on when the upgrade was scheduled (when createUpgrade() was | ||
* called) + AppStorage.upgradeExpiration. | ||
* @param duration The duration until the upgrade expires. | ||
*/ | ||
function updateUpgradeExpiration(uint256 duration) | ||
external | ||
assertPrivilege(LC.SYSTEM_IDENTIFIER_BYTES32, LC.GROUP_SYSTEM_ADMINS) | ||
{ | ||
AppStorage storage s = LibAppStorage.diamondStorage(); | ||
|
||
require(1 minutes < duration && duration < 1 weeks, "invalid upgrade expiration period"); | ||
|
||
s.upgradeExpiration = duration; | ||
emit UpdateUpgradeExpiration(duration); | ||
} | ||
|
||
/** | ||
* @notice Cancel the following upgrade hash: `id` | ||
* @dev This will set the mapping AppStorage.upgradeScheduled back to 0. | ||
* @param id This is the keccak256(abi.encode(cut)), where cut is the array of FacetCut struct, | ||
* IDiamondCut.FacetCut[]. | ||
*/ | ||
function cancelUpgrade(bytes32 id) external assertPrivilege(LC.SYSTEM_IDENTIFIER_BYTES32, LC.GROUP_SYSTEM_ADMINS) { | ||
AppStorage storage s = LibAppStorage.diamondStorage(); | ||
|
||
require(s.upgradeScheduled[id] > 0, "invalid upgrade ID"); | ||
|
||
s.upgradeScheduled[id] = 0; | ||
|
||
emit UpgradeCancelled(id, msg.sender); | ||
} | ||
|
||
/** | ||
* @notice Get the expiry date for provided upgrade hash. | ||
* @dev This will get the value from AppStorage.upgradeScheduled mapping. | ||
* @param id This is the keccak256(abi.encode(cut)), where cut is the array of FacetCut struct, | ||
* IDiamondCut.FacetCut[]. | ||
*/ | ||
function getUpgrade(bytes32 id) external view returns (uint256 expiry) { | ||
AppStorage storage s = LibAppStorage.diamondStorage(); | ||
expiry = s.upgradeScheduled[id]; | ||
} | ||
|
||
/** | ||
* @notice Get the upgrade expiration period. | ||
* @dev This will get the value from AppStorage.upgradeExpiration. AppStorage.upgradeExpiration is added to the | ||
* block.timestamp to create the upgrade expiration date. | ||
*/ | ||
function getUpgradeExpiration() external view returns (uint256 upgradeExpiration) { | ||
AppStorage storage s = LibAppStorage.diamondStorage(); | ||
upgradeExpiration = s.upgradeExpiration; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.20; | ||
|
||
import { LibDiamond } from "lib/diamond-2-hardhat/contracts/libraries/LibDiamond.sol"; | ||
import { IERC173 } from "lib/diamond-2-hardhat/contracts/interfaces/IERC173.sol"; | ||
import { LibACL } from "src/libs/LibACL.sol"; | ||
import { LibHelpers } from "src/libs/LibHelpers.sol"; | ||
import { LibAdmin } from "src/libs/LibAdmin.sol"; | ||
import { LibConstants as LC } from "src/libs/LibConstants.sol"; | ||
import { Modifiers } from "src/shared/Modifiers.sol"; | ||
|
||
contract NaymsOwnershipFacet is IERC173, Modifiers { | ||
function transferOwnership(address _newOwner) | ||
external | ||
override | ||
assertPrivilege(LC.SYSTEM_IDENTIFIER_BYTES32, LC.GROUP_SYSTEM_ADMINS) | ||
{ | ||
bytes32 systemID = LibHelpers._stringToBytes32(LC.SYSTEM_IDENTIFIER); | ||
bytes32 newAcc1Id = LibHelpers._getIdForAddress(_newOwner); | ||
|
||
require( | ||
!LibACL._isInGroup(newAcc1Id, systemID, LibHelpers._stringToBytes32(LC.GROUP_SYSTEM_ADMINS)), | ||
"NEW owner MUST NOT be sys admin" | ||
); | ||
require( | ||
!LibACL._isInGroup(newAcc1Id, systemID, LibHelpers._stringToBytes32(LC.GROUP_SYSTEM_MANAGERS)), | ||
"NEW owner MUST NOT be sys manager" | ||
); | ||
|
||
LibDiamond.setContractOwner(_newOwner); | ||
} | ||
|
||
function owner() external view override returns (address owner_) { | ||
owner_ = LibDiamond.contractOwner(); | ||
} | ||
} |
Oops, something went wrong.