1- // SPDX-License-Identifier: MIT
1+ // SPDX-License-Identifier: BUSL-1.1
22pragma solidity ^ 0.8.24 ;
33
4+ import {RoleRegistry} from "./RoleRegistry.sol " ;
5+ import {SecurityBase} from "../security/SecurityBase.sol " ;
6+
47/**
58 * @title AccessManager
6- * @notice TODO: Add contract description
9+ * @author BAOBAB Protocol
10+ * @notice Central access control for all protocol contracts
11+ * @dev Role-based access control with hierarchical permissions
12+ *
13+ * ═══════════════════════════════════════════════════════════════════════════════════════════════════
14+ * ACCESS MANAGER
15+ * ═══════════════════════════════════════════════════════════════════════════════════════════════════
716 */
8- contract AccessManager {
9- // TODO: Implement contract
10- }
17+ contract AccessManager is SecurityBase {
18+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
19+ // STATE VARIABLES
20+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
21+
22+ /// @notice Role memberships: role => account => isMember
23+ mapping (bytes32 => mapping (address => bool )) private _roles;
24+
25+ /// @notice Role admin: role => adminRole (who can grant/revoke this role)
26+ mapping (bytes32 => bytes32 ) private _roleAdmins;
27+
28+ /// @notice Account roles: account => roles[]
29+ mapping (address => bytes32 []) private _accountRoles;
30+
31+ /// @notice Role members: role => members[]
32+ mapping (bytes32 => address []) private _roleMembers;
33+
34+ /// @notice Protocol owner (highest authority)
35+ address public owner;
36+
37+ /// @notice Pending owner for two-step transfer
38+ address public pendingOwner;
39+
40+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
41+ // EVENTS
42+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
43+
44+ event RoleGranted (bytes32 indexed role , address indexed account , address indexed sender );
45+ event RoleRevoked (bytes32 indexed role , address indexed account , address indexed sender );
46+ event RoleAdminChanged (bytes32 indexed role , bytes32 indexed previousAdminRole , bytes32 indexed newAdminRole );
47+ event OwnershipTransferStarted (address indexed previousOwner , address indexed newOwner );
48+ event OwnershipTransferred (address indexed previousOwner , address indexed newOwner );
49+
50+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
51+ // ERRORS
52+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
53+
54+ error AccessManager__OnlyOwner ();
55+ error AccessManager__MissingRole (bytes32 role );
56+ error AccessManager__AlreadyHasRole ();
57+ error AccessManager__UnauthorizedRoleAdmin ();
58+ error AccessManager__CannotRevokeOwnRole ();
59+ error AccessManager__InvalidAddress ();
60+
61+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
62+ // CONSTRUCTOR
63+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
64+
65+ constructor (address _owner ) {
66+ if (_owner == address (0 )) revert AccessManager__InvalidAddress ();
67+
68+ owner = _owner;
69+
70+ // Grant owner the OWNER_ROLE
71+ _grantRole (RoleRegistry.OWNER_ROLE, _owner);
72+
73+ // Set OWNER_ROLE as admin of all roles
74+ _setRoleAdmin (RoleRegistry.OWNER_ROLE, RoleRegistry.OWNER_ROLE);
75+ _setRoleAdmin (RoleRegistry.ADMIN_ROLE, RoleRegistry.OWNER_ROLE);
76+ _setRoleAdmin (RoleRegistry.GUARDIAN_ROLE, RoleRegistry.OWNER_ROLE);
77+ _setRoleAdmin (RoleRegistry.KEEPER_ROLE, RoleRegistry.ADMIN_ROLE);
78+ _setRoleAdmin (RoleRegistry.LIQUIDATOR_ROLE, RoleRegistry.ADMIN_ROLE);
79+ _setRoleAdmin (RoleRegistry.ORACLE_UPDATER_ROLE, RoleRegistry.ADMIN_ROLE);
80+ _setRoleAdmin (RoleRegistry.MARKET_MAKER_ROLE, RoleRegistry.ADMIN_ROLE);
81+ _setRoleAdmin (RoleRegistry.FEE_MANAGER_ROLE, RoleRegistry.ADMIN_ROLE);
82+ _setRoleAdmin (RoleRegistry.BASKET_MANAGER_ROLE, RoleRegistry.ADMIN_ROLE);
83+ _setRoleAdmin (RoleRegistry.EVENT_SETTLER_ROLE, RoleRegistry.ADMIN_ROLE);
84+ _setRoleAdmin (RoleRegistry.PAUSER_ROLE, RoleRegistry.GUARDIAN_ROLE);
85+ _setRoleAdmin (RoleRegistry.UPGRADER_ROLE, RoleRegistry.OWNER_ROLE);
86+ _setRoleAdmin (RoleRegistry.TRADING_OPERATOR_ROLE, RoleRegistry.ADMIN_ROLE);
87+ _setRoleAdmin (RoleRegistry.VAULT_OPERATOR_ROLE, RoleRegistry.ADMIN_ROLE);
88+ _setRoleAdmin (RoleRegistry.RISK_MANAGER_ROLE, RoleRegistry.ADMIN_ROLE);
89+ }
90+
91+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
92+ // MODIFIERS
93+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
94+
95+ modifier onlyOwner () {
96+ if (msg .sender != owner) revert AccessManager__OnlyOwner ();
97+ _;
98+ }
99+
100+ modifier onlyRole (bytes32 role ) {
101+ if (! hasRole (role, msg .sender )) {
102+ revert AccessManager__MissingRole (role);
103+ }
104+ _;
105+ }
106+
107+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
108+ // ROLE MANAGEMENT
109+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
110+
111+ /**
112+ * @notice Grant role to account
113+ * @param role Role to grant
114+ * @param account Account receiving role
115+ * @dev Only callable by role admin
116+ */
117+ function grantRole (bytes32 role , address account ) external {
118+ if (! hasRole (_roleAdmins[role], msg .sender )) {
119+ revert AccessManager__UnauthorizedRoleAdmin ();
120+ }
121+ _grantRole (role, account);
122+ }
123+
124+ /**
125+ * @notice Revoke role from account
126+ * @param role Role to revoke
127+ * @param account Account losing role
128+ * @dev Only callable by role admin
129+ */
130+ function revokeRole (bytes32 role , address account ) external {
131+ if (! hasRole (_roleAdmins[role], msg .sender )) {
132+ revert AccessManager__UnauthorizedRoleAdmin ();
133+ }
134+ if (msg .sender == account) {
135+ revert AccessManager__CannotRevokeOwnRole ();
136+ }
137+ _revokeRole (role, account);
138+ }
139+
140+ /**
141+ * @notice Renounce own role
142+ * @param role Role to renounce
143+ * @dev Account voluntarily gives up role
144+ */
145+ function renounceRole (bytes32 role ) external {
146+ _revokeRole (role, msg .sender );
147+ }
148+
149+ /**
150+ * @notice Batch grant roles
151+ * @param roles Array of roles to grant
152+ * @param accounts Array of accounts receiving roles
153+ */
154+ function batchGrantRoles (
155+ bytes32 [] calldata roles ,
156+ address [] calldata accounts
157+ ) external {
158+ require (roles.length == accounts.length , "Length mismatch " );
159+
160+ for (uint256 i = 0 ; i < roles.length ; i++ ) {
161+ if (! hasRole (_roleAdmins[roles[i]], msg .sender )) {
162+ revert AccessManager__UnauthorizedRoleAdmin ();
163+ }
164+ _grantRole (roles[i], accounts[i]);
165+ }
166+ }
167+
168+ /**
169+ * @notice Batch revoke roles
170+ * @param roles Array of roles to revoke
171+ * @param accounts Array of accounts losing roles
172+ */
173+ function batchRevokeRoles (
174+ bytes32 [] calldata roles ,
175+ address [] calldata accounts
176+ ) external {
177+ require (roles.length == accounts.length , "Length mismatch " );
178+
179+ for (uint256 i = 0 ; i < roles.length ; i++ ) {
180+ if (! hasRole (_roleAdmins[roles[i]], msg .sender )) {
181+ revert AccessManager__UnauthorizedRoleAdmin ();
182+ }
183+ if (msg .sender == accounts[i]) {
184+ revert AccessManager__CannotRevokeOwnRole ();
185+ }
186+ _revokeRole (roles[i], accounts[i]);
187+ }
188+ }
189+
190+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
191+ // OWNERSHIP TRANSFER
192+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
193+
194+ /**
195+ * @notice Start ownership transfer (step 1 of 2)
196+ * @param newOwner New owner address
197+ * @dev Two-step process for safety
198+ */
199+ function transferOwnership (address newOwner ) external onlyOwner {
200+ if (newOwner == address (0 )) revert AccessManager__InvalidAddress ();
201+ pendingOwner = newOwner;
202+ emit OwnershipTransferStarted (owner, newOwner);
203+ }
204+
205+ /**
206+ * @notice Accept ownership transfer (step 2 of 2)
207+ * @dev Must be called by pending owner
208+ */
209+ function acceptOwnership () external {
210+ if (msg .sender != pendingOwner) revert AccessManager__OnlyOwner ();
211+
212+ address oldOwner = owner;
213+ owner = pendingOwner;
214+ pendingOwner = address (0 );
215+
216+ // Transfer OWNER_ROLE
217+ _revokeRole (RoleRegistry.OWNER_ROLE, oldOwner);
218+ _grantRole (RoleRegistry.OWNER_ROLE, owner);
219+
220+ emit OwnershipTransferred (oldOwner, owner);
221+ }
222+
223+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
224+ // INTERNAL FUNCTIONS
225+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
226+
227+ /**
228+ * @notice Internal grant role function
229+ * @param role Role to grant
230+ * @param account Account receiving role
231+ */
232+ function _grantRole (bytes32 role , address account ) internal {
233+ if (_roles[role][account]) revert AccessManager__AlreadyHasRole ();
234+
235+ _roles[role][account] = true ;
236+ _accountRoles[account].push (role);
237+ _roleMembers[role].push (account);
238+
239+ emit RoleGranted (role, account, msg .sender );
240+ }
241+
242+ /**
243+ * @notice Internal revoke role function
244+ * @param role Role to revoke
245+ * @param account Account losing role
246+ */
247+ function _revokeRole (bytes32 role , address account ) internal {
248+ if (! _roles[role][account]) return ;
249+
250+ _roles[role][account] = false ;
251+
252+ // Remove from account roles
253+ bytes32 [] storage accountRoles = _accountRoles[account];
254+ for (uint256 i = 0 ; i < accountRoles.length ; i++ ) {
255+ if (accountRoles[i] == role) {
256+ accountRoles[i] = accountRoles[accountRoles.length - 1 ];
257+ accountRoles.pop ();
258+ break ;
259+ }
260+ }
261+
262+ // Remove from role members
263+ address [] storage members = _roleMembers[role];
264+ for (uint256 i = 0 ; i < members.length ; i++ ) {
265+ if (members[i] == account) {
266+ members[i] = members[members.length - 1 ];
267+ members.pop ();
268+ break ;
269+ }
270+ }
271+
272+ emit RoleRevoked (role, account, msg .sender );
273+ }
274+
275+ /**
276+ * @notice Set role admin
277+ * @param role Role to configure
278+ * @param adminRole Admin role for this role
279+ */
280+ function _setRoleAdmin (bytes32 role , bytes32 adminRole ) internal {
281+ bytes32 previousAdminRole = _roleAdmins[role];
282+ _roleAdmins[role] = adminRole;
283+ emit RoleAdminChanged (role, previousAdminRole, adminRole);
284+ }
285+
286+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
287+ // VIEW FUNCTIONS
288+ // ═══════════════════════════════════════════════════════════════════════════════════════════════
289+
290+ /**
291+ * @notice Check if account has role
292+ * @param role Role to check
293+ * @param account Account to check
294+ * @return bool True if account has role
295+ */
296+ function hasRole (bytes32 role , address account ) public view returns (bool ) {
297+ return _roles[role][account];
298+ }
299+
300+ /**
301+ * @notice Get role admin
302+ * @param role Role to query
303+ * @return bytes32 Admin role identifier
304+ */
305+ function getRoleAdmin (bytes32 role ) external view returns (bytes32 ) {
306+ return _roleAdmins[role];
307+ }
308+
309+ /**
310+ * @notice Get all roles for an account
311+ * @param account Account to query
312+ * @return roles Array of role identifiers
313+ */
314+ function getAccountRoles (address account ) external view returns (bytes32 [] memory roles ) {
315+ return _accountRoles[account];
316+ }
317+
318+ /**
319+ * @notice Get all members of a role
320+ * @param role Role to query
321+ * @return members Array of addresses
322+ */
323+ function getRoleMembers (bytes32 role ) external view returns (address [] memory members ) {
324+ return _roleMembers[role];
325+ }
326+
327+ /**
328+ * @notice Get role member count
329+ * @param role Role to query
330+ * @return count Number of members
331+ */
332+ function getRoleMemberCount (bytes32 role ) external view returns (uint256 count ) {
333+ return _roleMembers[role].length ;
334+ }
335+
336+ /**
337+ * @notice Check if account has any of the roles
338+ * @param roles Array of roles to check
339+ * @param account Account to check
340+ * @return bool True if account has at least one role
341+ */
342+ function hasAnyRole (bytes32 [] calldata roles , address account ) external view returns (bool ) {
343+ for (uint256 i = 0 ; i < roles.length ; i++ ) {
344+ if (hasRole (roles[i], account)) return true ;
345+ }
346+ return false ;
347+ }
348+
349+ /**
350+ * @notice Check if account has all roles
351+ * @param roles Array of roles to check
352+ * @param account Account to check
353+ * @return bool True if account has all roles
354+ */
355+ function hasAllRoles (bytes32 [] calldata roles , address account ) external view returns (bool ) {
356+ for (uint256 i = 0 ; i < roles.length ; i++ ) {
357+ if (! hasRole (roles[i], account)) return false ;
358+ }
359+ return true ;
360+ }
361+ }
0 commit comments