Skip to content
Open
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ out/
/broadcast/*/31337/
/broadcast/**/dry-run/

soljson-latest.js

# Dotenv file
.env
.gas-snapshot
Expand All @@ -16,7 +18,6 @@ node_modules/
# Mirror of root CHANGELOG.md for Changesets
src/CHANGELOG.md


# Docusaurus
# Dependencies
website/node_modules
Expand Down
94 changes: 88 additions & 6 deletions src/access/AccessControl/Admin/AccessControlAdminFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,34 @@ contract AccessControlAdminFacet {
*/
error AccessControlUnauthorizedAccount(address _account, bytes32 _role);

/**
* @notice Thrown when a role has expired.
* @param _role The role that has expired.
* @param _account The account whose role has expired.
*/
error AccessControlRoleExpired(bytes32 _role, address _account);

/**
* @notice Thrown when a role is paused and an operation requiring that role is attempted.
* @param _role The role that is paused.
*/
error AccessControlRolePaused(bytes32 _role);

/**
* @notice Storage slot identifier.
*/
bytes32 constant STORAGE_POSITION = keccak256("compose.accesscontrol");

/**
* @notice Storage slot identifier for Temporal functionality.
*/
bytes32 constant TEMPORAL_STORAGE_POSITION = keccak256("compose.accesscontrol.temporal");

/**
* @notice Storage slot identifier for Pausable functionality.
*/
bytes32 constant PAUSABLE_STORAGE_POSITION = keccak256("compose.accesscontrol.pausable");

/**
* @notice Storage struct for the AccessControl.
* @custom:storage-location erc8042:compose.accesscontrol
Expand All @@ -35,6 +58,22 @@ contract AccessControlAdminFacet {
mapping(bytes32 role => bytes32 adminRole) adminRole;
}

/**
* @notice Storage struct for AccessControlTemporal.
* @custom:storage-location erc8042:compose.accesscontrol.temporal
*/
struct AccessControlTemporalStorage {
mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry;
}

/**
* @notice Storage struct for AccessControlPausable.
* @custom:storage-location erc8042:compose.accesscontrol.pausable
*/
struct AccessControlPausableStorage {
mapping(bytes32 role => bool paused) pausedRoles;
}

/**
* @notice Returns the storage for the AccessControl.
* @return s The storage for the AccessControl.
Expand All @@ -46,6 +85,54 @@ contract AccessControlAdminFacet {
}
}

/**
* @notice Returns the storage for AccessControlTemporal.
* @return s The AccessControlTemporal storage struct.
*/
function getTemporalStorage() internal pure returns (AccessControlTemporalStorage storage s) {
bytes32 position = TEMPORAL_STORAGE_POSITION;
assembly {
s.slot := position
}
}

/**
* @notice Returns the storage for AccessControlPausable.
* @return s The AccessControlPausable storage struct.
*/
function getPausableStorage() internal pure returns (AccessControlPausableStorage storage s) {
bytes32 position = PAUSABLE_STORAGE_POSITION;
assembly {
s.slot := position
}
}

/**
* @notice Requires the caller to have a specific role that has not expired and is not paused.
* @param _role The role that the caller must have.
* @dev Reverts with {AccessControlUnauthorizedAccount} if the caller does not have the role.
* @dev Reverts with {AccessControlRoleExpired} if the caller's role has expired.
* @dev Reverts with {AccessControlRolePaused} if the role is paused.
*/
function _requireRole(bytes32 _role) internal view {
AccessControlStorage storage s = getStorage();

if (!s.hasRole[msg.sender][_role]) {
revert AccessControlUnauthorizedAccount(msg.sender, _role);
}

AccessControlTemporalStorage storage ts = getTemporalStorage();
uint256 expiry = ts.roleExpiry[msg.sender][_role];
if (expiry > 0 && block.timestamp >= expiry) {
revert AccessControlRoleExpired(_role, msg.sender);
}

AccessControlPausableStorage storage ps = getPausableStorage();
if (ps.pausedRoles[_role]) {
revert AccessControlRolePaused(_role);
}
}

/**
* @notice Sets the admin role for a role.
* @param _role The role to set the admin for.
Expand All @@ -57,12 +144,7 @@ contract AccessControlAdminFacet {
AccessControlStorage storage s = getStorage();
bytes32 previousAdminRole = s.adminRole[_role];

/**
* Check if the caller is the current admin of the role.
*/
if (!s.hasRole[msg.sender][previousAdminRole]) {
revert AccessControlUnauthorizedAccount(msg.sender, previousAdminRole);
}
_requireRole(previousAdminRole);

s.adminRole[_role] = _adminRole;
emit RoleAdminChanged(_role, previousAdminRole, _adminRole);
Expand Down
94 changes: 88 additions & 6 deletions src/access/AccessControl/Admin/AccessControlAdminMod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,34 @@ event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole
*/
error AccessControlUnauthorizedAccount(address _account, bytes32 _role);

/**
* @notice Thrown when a role has expired.
* @param _role The role that has expired.
* @param _account The account whose role has expired.
*/
error AccessControlRoleExpired(bytes32 _role, address _account);

/**
* @notice Thrown when a role is paused and an operation requiring that role is attempted.
* @param _role The role that is paused.
*/
error AccessControlRolePaused(bytes32 _role);

/*
* @notice Storage slot identifier.
*/
bytes32 constant STORAGE_POSITION = keccak256("compose.accesscontrol");

/*
* @notice Storage slot identifier for Temporal functionality.
*/
bytes32 constant TEMPORAL_STORAGE_POSITION = keccak256("compose.accesscontrol.temporal");

/*
* @notice Storage slot identifier for Pausable functionality.
*/
bytes32 constant PAUSABLE_STORAGE_POSITION = keccak256("compose.accesscontrol.pausable");

/**
* @notice Storage struct for the AccessControl.
* @custom:storage-location erc8042:compose.accesscontrol
Expand All @@ -34,6 +57,22 @@ struct AccessControlStorage {
mapping(bytes32 role => bytes32 adminRole) adminRole;
}

/**
* @notice Storage struct for AccessControlTemporal.
* @custom:storage-location erc8042:compose.accesscontrol.temporal
*/
struct AccessControlTemporalStorage {
mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry;
}

/**
* @notice Storage struct for AccessControlPausable.
* @custom:storage-location erc8042:compose.accesscontrol.pausable
*/
struct AccessControlPausableStorage {
mapping(bytes32 role => bool paused) pausedRoles;
}

/**
* @notice Returns the storage for the AccessControl.
* @return s The storage for the AccessControl.
Expand All @@ -45,6 +84,54 @@ function getStorage() pure returns (AccessControlStorage storage s) {
}
}

/**
* @notice Returns the storage for AccessControlTemporal.
* @return s The AccessControlTemporal storage struct.
*/
function getTemporalStorage() pure returns (AccessControlTemporalStorage storage s) {
bytes32 position = TEMPORAL_STORAGE_POSITION;
assembly {
s.slot := position
}
}

/**
* @notice Returns the storage for AccessControlPausable.
* @return s The AccessControlPausable storage struct.
*/
function getPausableStorage() pure returns (AccessControlPausableStorage storage s) {
bytes32 position = PAUSABLE_STORAGE_POSITION;
assembly {
s.slot := position
}
}

/**
* @notice Requires the caller to have a specific role that has not expired and is not paused.
* @param _role The role that the caller must have.
* @dev Reverts with {AccessControlUnauthorizedAccount} if the caller does not have the role.
* @dev Reverts with {AccessControlRoleExpired} if the caller's role has expired.
* @dev Reverts with {AccessControlRolePaused} if the role is paused.
*/
function _requireRole(bytes32 _role) view {
AccessControlStorage storage s = getStorage();

if (!s.hasRole[msg.sender][_role]) {
revert AccessControlUnauthorizedAccount(msg.sender, _role);
}

AccessControlTemporalStorage storage ts = getTemporalStorage();
uint256 expiry = ts.roleExpiry[msg.sender][_role];
if (expiry > 0 && block.timestamp >= expiry) {
revert AccessControlRoleExpired(_role, msg.sender);
}

AccessControlPausableStorage storage ps = getPausableStorage();
if (ps.pausedRoles[_role]) {
revert AccessControlRolePaused(_role);
}
}

/**
* @notice Sets the admin role for a role.
* @param _role The role to set the admin for.
Expand All @@ -56,12 +143,7 @@ function setRoleAdmin(bytes32 _role, bytes32 _adminRole) {
AccessControlStorage storage s = getStorage();
bytes32 previousAdminRole = s.adminRole[_role];

/**
* Check if the caller is the current admin of the role.
*/
if (!s.hasRole[msg.sender][previousAdminRole]) {
revert AccessControlUnauthorizedAccount(msg.sender, previousAdminRole);
}
_requireRole(previousAdminRole);

s.adminRole[_role] = _adminRole;
emit RoleAdminChanged(_role, previousAdminRole, _adminRole);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ contract AccessControlGrantBatchFacet {
*/
error AccessControlUnauthorizedAccount(address _account, bytes32 _role);

/**
* @notice Thrown when a role has expired.
* @param _role The role that has expired.
* @param _account The account whose role has expired.
*/
error AccessControlRoleExpired(bytes32 _role, address _account);

/**
* @notice Thrown when a role is paused and an operation requiring that role is attempted.
* @param _role The role that is paused.
*/
error AccessControlRolePaused(bytes32 _role);

/**
* @notice Emitted when a role is granted to an account.
* @param _role The role that was granted.
Expand All @@ -26,6 +39,16 @@ contract AccessControlGrantBatchFacet {
*/
bytes32 constant STORAGE_POSITION = keccak256("compose.accesscontrol");

/**
* @notice Storage slot identifier for Temporal functionality.
*/
bytes32 constant TEMPORAL_STORAGE_POSITION = keccak256("compose.accesscontrol.temporal");

/**
* @notice Storage slot identifier for Pausable functionality.
*/
bytes32 constant PAUSABLE_STORAGE_POSITION = keccak256("compose.accesscontrol.pausable");

/**
* @notice Storage struct for the AccessControl.
* @custom:storage-location erc8042:compose.accesscontrol
Expand All @@ -35,6 +58,22 @@ contract AccessControlGrantBatchFacet {
mapping(bytes32 role => bytes32 adminRole) adminRole;
}

/**
* @notice Storage struct for AccessControlTemporal.
* @custom:storage-location erc8042:compose.accesscontrol.temporal
*/
struct AccessControlTemporalStorage {
mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry;
}

/**
* @notice Storage struct for AccessControlPausable.
* @custom:storage-location erc8042:compose.accesscontrol.pausable
*/
struct AccessControlPausableStorage {
mapping(bytes32 role => bool paused) pausedRoles;
}

/**
* @notice Returns the storage for the AccessControl.
* @return s The storage for the AccessControl.
Expand All @@ -46,6 +85,54 @@ contract AccessControlGrantBatchFacet {
}
}

/**
* @notice Returns the storage for AccessControlTemporal.
* @return s The AccessControlTemporal storage struct.
*/
function getTemporalStorage() internal pure returns (AccessControlTemporalStorage storage s) {
bytes32 position = TEMPORAL_STORAGE_POSITION;
assembly {
s.slot := position
}
}

/**
* @notice Returns the storage for AccessControlPausable.
* @return s The AccessControlPausable storage struct.
*/
function getPausableStorage() internal pure returns (AccessControlPausableStorage storage s) {
bytes32 position = PAUSABLE_STORAGE_POSITION;
assembly {
s.slot := position
}
}

/**
* @notice Requires the caller to have a specific role that has not expired and is not paused.
* @param _role The role that the caller must have.
* @dev Reverts with {AccessControlUnauthorizedAccount} if the caller does not have the role.
* @dev Reverts with {AccessControlRoleExpired} if the caller's role has expired.
* @dev Reverts with {AccessControlRolePaused} if the role is paused.
*/
function _requireRole(bytes32 _role) internal view {
AccessControlStorage storage s = getStorage();

if (!s.hasRole[msg.sender][_role]) {
revert AccessControlUnauthorizedAccount(msg.sender, _role);
}

AccessControlTemporalStorage storage ts = getTemporalStorage();
uint256 expiry = ts.roleExpiry[msg.sender][_role];
if (expiry > 0 && block.timestamp >= expiry) {
revert AccessControlRoleExpired(_role, msg.sender);
}

AccessControlPausableStorage storage ps = getPausableStorage();
if (ps.pausedRoles[_role]) {
revert AccessControlRolePaused(_role);
}
}

/**
* @notice Grants a role to multiple accounts in a single transaction.
* @param _role The role to grant.
Expand All @@ -57,12 +144,7 @@ contract AccessControlGrantBatchFacet {
AccessControlStorage storage s = getStorage();
bytes32 adminRole = s.adminRole[_role];

/**
* Check if the caller is the admin of the role.
*/
if (!s.hasRole[msg.sender][adminRole]) {
revert AccessControlUnauthorizedAccount(msg.sender, adminRole);
}
_requireRole(adminRole);

uint256 length = _accounts.length;
for (uint256 i = 0; i < length; i++) {
Expand Down
Loading
Loading