- Created .gitignore to exclude sensitive files and directories. - Added API documentation in API_DOCUMENTATION.md. - Included deployment instructions in DEPLOYMENT.md. - Established project structure documentation in PROJECT_STRUCTURE.md. - Updated README.md with project status and team information. - Added recommendations and status tracking documents. - Introduced testing guidelines in TESTING.md. - Set up CI workflow in .github/workflows/ci.yml. - Created Dockerfile for backend and frontend setups. - Added various service and utility files for backend functionality. - Implemented frontend components and pages for user interface. - Included mobile app structure and services. - Established scripts for deployment across multiple chains.
219 lines
8.4 KiB
Solidity
219 lines
8.4 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.24;
|
|
|
|
/**
|
|
* @title LibAccessControl
|
|
* @notice Diamond-compatible access control library using Diamond storage pattern
|
|
* @dev Provides role-based access control for Diamond facets
|
|
*/
|
|
library LibAccessControl {
|
|
bytes32 constant ACCESS_CONTROL_STORAGE_POSITION = keccak256("asle.accesscontrol.storage");
|
|
bytes32 constant TIMELOCK_STORAGE_POSITION = keccak256("asle.timelock.storage");
|
|
|
|
// Role definitions
|
|
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
|
|
bytes32 public constant POOL_CREATOR_ROLE = keccak256("POOL_CREATOR_ROLE");
|
|
bytes32 public constant VAULT_CREATOR_ROLE = keccak256("VAULT_CREATOR_ROLE");
|
|
bytes32 public constant COMPLIANCE_ADMIN_ROLE = keccak256("COMPLIANCE_ADMIN_ROLE");
|
|
bytes32 public constant GOVERNANCE_ADMIN_ROLE = keccak256("GOVERNANCE_ADMIN_ROLE");
|
|
bytes32 public constant SECURITY_ADMIN_ROLE = keccak256("SECURITY_ADMIN_ROLE");
|
|
bytes32 public constant FEE_COLLECTOR_ROLE = keccak256("FEE_COLLECTOR_ROLE");
|
|
|
|
struct RoleData {
|
|
mapping(address => bool) members;
|
|
bytes32 adminRole;
|
|
}
|
|
|
|
struct AccessControlStorage {
|
|
mapping(bytes32 => RoleData) roles;
|
|
address[] roleMembers; // For enumeration support
|
|
}
|
|
|
|
struct TimelockStorage {
|
|
mapping(bytes32 => uint256) scheduledOperations; // operationId => executionTime
|
|
uint256 defaultDelay; // Default timelock delay in seconds
|
|
bool timelockEnabled;
|
|
}
|
|
|
|
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
|
|
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
|
|
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
|
|
event OperationScheduled(bytes32 indexed operationId, uint256 executionTime);
|
|
event OperationExecuted(bytes32 indexed operationId);
|
|
|
|
function accessControlStorage() internal pure returns (AccessControlStorage storage acs) {
|
|
bytes32 position = ACCESS_CONTROL_STORAGE_POSITION;
|
|
assembly {
|
|
acs.slot := position
|
|
}
|
|
}
|
|
|
|
function timelockStorage() internal pure returns (TimelockStorage storage ts) {
|
|
bytes32 position = TIMELOCK_STORAGE_POSITION;
|
|
assembly {
|
|
ts.slot := position
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @notice Check if an account has a specific role
|
|
*/
|
|
function hasRole(bytes32 role, address account) internal view returns (bool) {
|
|
return accessControlStorage().roles[role].members[account];
|
|
}
|
|
|
|
/**
|
|
* @notice Check if account has role or is admin of role
|
|
*/
|
|
function hasRoleOrAdmin(bytes32 role, address account) internal view returns (bool) {
|
|
AccessControlStorage storage acs = accessControlStorage();
|
|
return acs.roles[role].members[account] || hasRole(getRoleAdmin(role), account);
|
|
}
|
|
|
|
/**
|
|
* @notice Get the admin role for a given role
|
|
*/
|
|
function getRoleAdmin(bytes32 role) internal view returns (bytes32) {
|
|
AccessControlStorage storage acs = accessControlStorage();
|
|
bytes32 adminRole = acs.roles[role].adminRole;
|
|
return adminRole == bytes32(0) ? DEFAULT_ADMIN_ROLE : adminRole;
|
|
}
|
|
|
|
/**
|
|
* @notice Grant a role to an account
|
|
* @dev Can only be called by accounts with admin role
|
|
*/
|
|
function grantRole(bytes32 role, address account) internal {
|
|
AccessControlStorage storage acs = accessControlStorage();
|
|
bytes32 adminRole = getRoleAdmin(role);
|
|
require(hasRole(adminRole, msg.sender), "LibAccessControl: account is missing admin role");
|
|
|
|
if (!acs.roles[role].members[account]) {
|
|
acs.roles[role].members[account] = true;
|
|
emit RoleGranted(role, account, msg.sender);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @notice Revoke a role from an account
|
|
* @dev Can only be called by accounts with admin role
|
|
*/
|
|
function revokeRole(bytes32 role, address account) internal {
|
|
AccessControlStorage storage acs = accessControlStorage();
|
|
bytes32 adminRole = getRoleAdmin(role);
|
|
require(hasRole(adminRole, msg.sender), "LibAccessControl: account is missing admin role");
|
|
|
|
if (acs.roles[role].members[account]) {
|
|
acs.roles[role].members[account] = false;
|
|
emit RoleRevoked(role, account, msg.sender);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @notice Set the admin role for a role
|
|
*/
|
|
function setRoleAdmin(bytes32 role, bytes32 adminRole) internal {
|
|
AccessControlStorage storage acs = accessControlStorage();
|
|
bytes32 previousAdminRole = getRoleAdmin(role);
|
|
require(hasRole(previousAdminRole, msg.sender), "LibAccessControl: account is missing admin role");
|
|
|
|
acs.roles[role].adminRole = adminRole;
|
|
emit RoleAdminChanged(role, previousAdminRole, adminRole);
|
|
}
|
|
|
|
/**
|
|
* @notice Require that account has role, revert if not
|
|
*/
|
|
function requireRole(bytes32 role, address account) internal view {
|
|
require(hasRole(role, account), "LibAccessControl: account is missing role");
|
|
}
|
|
|
|
/**
|
|
* @notice Initialize access control with default admin
|
|
*/
|
|
function initializeAccessControl(address defaultAdmin) internal {
|
|
AccessControlStorage storage acs = accessControlStorage();
|
|
require(!acs.roles[DEFAULT_ADMIN_ROLE].members[defaultAdmin], "LibAccessControl: already initialized");
|
|
|
|
acs.roles[DEFAULT_ADMIN_ROLE].members[defaultAdmin] = true;
|
|
|
|
// Set role hierarchies
|
|
acs.roles[POOL_CREATOR_ROLE].adminRole = DEFAULT_ADMIN_ROLE;
|
|
acs.roles[VAULT_CREATOR_ROLE].adminRole = DEFAULT_ADMIN_ROLE;
|
|
acs.roles[COMPLIANCE_ADMIN_ROLE].adminRole = DEFAULT_ADMIN_ROLE;
|
|
acs.roles[GOVERNANCE_ADMIN_ROLE].adminRole = DEFAULT_ADMIN_ROLE;
|
|
acs.roles[SECURITY_ADMIN_ROLE].adminRole = DEFAULT_ADMIN_ROLE;
|
|
acs.roles[FEE_COLLECTOR_ROLE].adminRole = DEFAULT_ADMIN_ROLE;
|
|
|
|
emit RoleGranted(DEFAULT_ADMIN_ROLE, defaultAdmin, address(0));
|
|
}
|
|
|
|
// ============ Timelock Functions ============
|
|
|
|
/**
|
|
* @notice Schedule an operation with timelock
|
|
*/
|
|
function scheduleOperation(bytes32 operationId, bytes32 operationHash) internal {
|
|
TimelockStorage storage ts = timelockStorage();
|
|
require(ts.timelockEnabled, "LibAccessControl: timelock not enabled");
|
|
require(ts.scheduledOperations[operationId] == 0, "LibAccessControl: operation already scheduled");
|
|
|
|
uint256 executionTime = block.timestamp + ts.defaultDelay;
|
|
ts.scheduledOperations[operationId] = executionTime;
|
|
|
|
emit OperationScheduled(operationId, executionTime);
|
|
}
|
|
|
|
/**
|
|
* @notice Check if operation is ready to execute
|
|
*/
|
|
function isOperationReady(bytes32 operationId) internal view returns (bool) {
|
|
TimelockStorage storage ts = timelockStorage();
|
|
if (!ts.timelockEnabled) return true;
|
|
|
|
uint256 executionTime = ts.scheduledOperations[operationId];
|
|
return executionTime > 0 && block.timestamp >= executionTime;
|
|
}
|
|
|
|
/**
|
|
* @notice Execute a scheduled operation
|
|
*/
|
|
function executeOperation(bytes32 operationId) internal {
|
|
TimelockStorage storage ts = timelockStorage();
|
|
require(isOperationReady(operationId), "LibAccessControl: operation not ready");
|
|
|
|
delete ts.scheduledOperations[operationId];
|
|
emit OperationExecuted(operationId);
|
|
}
|
|
|
|
/**
|
|
* @notice Cancel a scheduled operation
|
|
*/
|
|
function cancelOperation(bytes32 operationId) internal {
|
|
TimelockStorage storage ts = timelockStorage();
|
|
require(ts.scheduledOperations[operationId] > 0, "LibAccessControl: operation not scheduled");
|
|
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "LibAccessControl: must be admin");
|
|
|
|
delete ts.scheduledOperations[operationId];
|
|
}
|
|
|
|
/**
|
|
* @notice Set timelock delay
|
|
*/
|
|
function setTimelockDelay(uint256 delay) internal {
|
|
TimelockStorage storage ts = timelockStorage();
|
|
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "LibAccessControl: must be admin");
|
|
ts.defaultDelay = delay;
|
|
}
|
|
|
|
/**
|
|
* @notice Enable/disable timelock
|
|
*/
|
|
function setTimelockEnabled(bool enabled) internal {
|
|
TimelockStorage storage ts = timelockStorage();
|
|
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "LibAccessControl: must be admin");
|
|
ts.timelockEnabled = enabled;
|
|
}
|
|
}
|
|
|