@lazy-sol/access-control 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ v1.1.3: Prem's audit and its resolution
2
+ - See the list of issues found and resolved in [the audit resolution doc](./audits/1.1_Prem_resolution.md)
3
+ - See the audit methodology and issues found in [the original audit report](./audits/1.1_final_Prem.pdf)
4
+
1
5
  v1.1.2: do not enable full privileges to zero address on construction
2
6
 
3
7
  v1.1.1: Role-based Access Control (RBAC) Inspector
package/CONTRIBUTING.md CHANGED
@@ -197,4 +197,5 @@ See also:
197
197
 
198
198
  Prepared by Basil Gorin
199
199
 
200
- (c) 2017–2024 Basil Gorin
200
+ (c) 2017–2024 Basil Gorin
201
+ (c) 2024–2025 Lazy So\[u\]l
package/LICENSE.txt CHANGED
@@ -1,6 +1,7 @@
1
1
  The MIT License (MIT)
2
2
 
3
3
  Copyright (c) 2017-2024 Basil Gorin
4
+ Copyright (c) 2024–2025 Lazy So[u]l
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining
6
7
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -4,6 +4,10 @@ A shortcut to a modular and easily pluggable dapp architecture.
4
4
  Enable the modular plug and play (PnP) architecture for your dapp by incorporating the role-based access control (RBAC)
5
5
  into the smart contracts.
6
6
 
7
+ ## Audit(s)
8
+ * [v1.1 Audit by Prem, December 23, 2024 – January 20, 2025](./audits/1.1_final_Prem.pdf)
9
+ * [Resolution: v1.1 Audit by Prem](./audits/1.1_Prem_resolution.md)
10
+
7
11
  ## Technical Overview
8
12
 
9
13
  Role-based Access Control (RBAC), or simply Access Control, is the base parent contract to be inherited by other smart
@@ -267,9 +271,14 @@ To execute the restricted access function on the target contract via the AccessC
267
271
  3. To find **all** the addresses having any permissions, track the `RoleUpdated()` event and evaluate the history
268
272
  of `assiged` roles for every `operator` address
269
273
  * Alternatively, use the [tool](ui.html) which automates the process
274
+ (see demo [here](https://lazy-sol.github.io/access-control/ui.html))
275
+
276
+ ## See Also
277
+ [Upgradeable Role-based Access Control (U-RBAC)](https://github.com/lazy-sol/access-control-upgradeable/blob/master/README.md)
270
278
 
271
279
  ## Contributing
272
280
  Please see the [Contribution Guide](./CONTRIBUTING.md) document to get understanding on how to report issues,
273
281
  contribute to the source code, fix bugs, introduce new features, etc.
274
282
 
275
- (c) 2017–2024 Basil Gorin
283
+ (c) 2017–2024 Basil Gorin
284
+ (c) 2024–2025 Lazy So\[u\]l
@@ -0,0 +1,60 @@
1
+ # AccessControl: Smart Contract Audit Report Resolution #
2
+
3
+ ## Resolution Summary ##
4
+
5
+ | ID | | Resolution |
6
+ |---------|--------------------------------------------------------------------------------------------|--------------|
7
+ | Major-1 | Low-Level Call in `execute(bytes)` (OwnableToAccessControlAdapter) | Fixed |
8
+ | Major-2 | Validation of `targetAddress` in `deployNewOwnableToAccessControlAdapter` (AdapterFactory) | Fixed |
9
+ | Minor-1 | Insufficient Event Logging in `RoleUpdated` | Fixed |
10
+ | Minor-2 | No Check for `role != 0` in `updateRole` | Mitigated |
11
+ | Minor-3 | State Variables Could Be Declared immutable (OwnableToAccessControlAdapter) | Fixed |
12
+ | Notes-1 | Version Constraints with Known Issues | Mitigated |
13
+ | Notes-2 | Visibility Optimization for Gas Savings | Fixed |
14
+ | Notes-3 | Potential Improvement for `FULL_PRIVILEGES_MASK` Assignment | Acknowledged |
15
+
16
+ For issues which were ignored, acknowledged, mitigated, or fixed differently than suggested by the auditor, see the
17
+ [Comments](#comments) section below.
18
+
19
+ ## Comments ##
20
+ ### Major-1. Low-Level Call in `execute(bytes)` (OwnableToAccessControlAdapter) ###
21
+ Fixed by
22
+ 1. making `target` immutable,
23
+ 2. ensuring it is an already deployed contract in the `OwnableToAccessControlAdapter` constructor:
24
+ ```solidity
25
+ require(_target.code.length != 0, "EOA");
26
+ ```
27
+ 3. porting `_revert` function from OZ 4.9.6 `@openzeppelin/contracts/utils/Address.sol` library and using it
28
+ to keep original error messages from the target contract:
29
+ ```solidity
30
+ _revert(returndata, "execution failed");
31
+ ```
32
+
33
+ ### Minor-2. No Check for `role != 0` in `updateRole` ###
34
+ To be used as a parent contract for RBAC-based applications, the `AccessControl` contract is originally designed to be
35
+ lightweight, which was improved even further in version 1.1 by introducing the `AccessControlCore` contract.
36
+
37
+ To minimise contract size, we minimise the number of public functions exposed and use a single public function
38
+ `updateRole` to add, modify, and delete permissions, including self-revoke.
39
+
40
+ Mitigated by adding an explicit and very well noticeable comment in the SolDoc for `updateRole` function.
41
+
42
+ ### Notes-1. Version Constraints with Known Issues ###
43
+ To allow the use as a parent contract for a wide range of RBAC-based applications, and serve as a Solidity library,
44
+ we try to keep pragma constraint as low as possible. This approach maximizes compatibility.
45
+
46
+ Mitigated by updating the compiler version to 0.8.28 in `hardhat.config.js`.
47
+
48
+ ### Notes-2. Visibility Optimization for Gas Savings
49
+ Visibility modifier was changed from `public` to `external` for functions `isFeatureEnabled`, `isSenderInRole`,
50
+ `isOperatorInRole`, `updateFeatures`, `updateRole`, `updateAccessRole`, and `deployNewOwnableToAccessControlAdapter`.
51
+
52
+ Functions `features`, and `getRole` remain public to be accessible in inheriting contracts.
53
+
54
+ ### Notes-3. Potential Improvement for `FULL_PRIVILEGES_MASK` Assignment ###
55
+ Role-based Access Control (RBAC) library, and its AccessControl* contracts are designed to be a long-term, but still
56
+ temporary solution for the projects evolving in the direction of the fully decentralized operation. RBAC Lifecycle
57
+ assumes that all the permissions are eventually either fully revoked from external participants, or are fully
58
+ transitioned to the DAO governance smart contract.
59
+
60
+ Thus, further improvements to `FULL_PRIVILEGES_MASK` assignments and management are out of scope for the RBAC library.
Binary file
@@ -71,7 +71,7 @@ abstract contract AccessControl is AccessControlCore {
71
71
  * @param required set of features to check against
72
72
  * @return true if all the features requested are enabled, false otherwise
73
73
  */
74
- function isFeatureEnabled(uint256 required) public view returns (bool) {
74
+ function isFeatureEnabled(uint256 required) external view returns (bool) {
75
75
  // delegate to internal `_isFeatureEnabled`
76
76
  return _isFeatureEnabled(required);
77
77
  }
@@ -82,7 +82,7 @@ abstract contract AccessControl is AccessControlCore {
82
82
  * @param required set of permissions (role) to check against
83
83
  * @return true if all the permissions requested are enabled, false otherwise
84
84
  */
85
- function isSenderInRole(uint256 required) public view returns (bool) {
85
+ function isSenderInRole(uint256 required) external view returns (bool) {
86
86
  // delegate to internal `_isSenderInRole`
87
87
  return _isSenderInRole(required);
88
88
  }
@@ -94,7 +94,7 @@ abstract contract AccessControl is AccessControlCore {
94
94
  * @param required set of permissions (role) to check
95
95
  * @return true if all the permissions requested are enabled, false otherwise
96
96
  */
97
- function isOperatorInRole(address operator, uint256 required) public view returns (bool) {
97
+ function isOperatorInRole(address operator, uint256 required) external view returns (bool) {
98
98
  // delegate to internal `_isOperatorInRole`
99
99
  return _isOperatorInRole(operator, required);
100
100
  }
@@ -107,11 +107,12 @@ abstract contract AccessControlCore {
107
107
  /**
108
108
  * @dev Fired in updateRole() and updateFeatures()
109
109
  *
110
+ * @param by address which has granted/revoked permissions to operator
110
111
  * @param operator address which was granted/revoked permissions
111
112
  * @param requested permissions requested
112
113
  * @param assigned permissions effectively set
113
114
  */
114
- event RoleUpdated(address indexed operator, uint256 requested, uint256 assigned);
115
+ event RoleUpdated(address indexed by, address indexed operator, uint256 requested, uint256 assigned);
115
116
 
116
117
  /**
117
118
  * @notice Function modifier making a function defined as public behave as restricted
@@ -151,7 +152,7 @@ abstract contract AccessControlCore {
151
152
  *
152
153
  * @return 256-bit bitmask of the features enabled
153
154
  */
154
- function features() public view returns (uint256) {
155
+ function features() public view returns(uint256) {
155
156
  // features are stored in 'this' address mapping of `userRoles`
156
157
  return getRole(address(this));
157
158
  }
@@ -165,9 +166,9 @@ abstract contract AccessControlCore {
165
166
  *
166
167
  * @param _mask bitmask representing a set of features to enable/disable
167
168
  */
168
- function updateFeatures(uint256 _mask) public {
169
- // delegate call to `updateRole`
170
- updateRole(address(this), _mask);
169
+ function updateFeatures(uint256 _mask) external {
170
+ // delegate to internal `_updateRole()`
171
+ _updateRole(address(this), _mask);
171
172
  }
172
173
 
173
174
  /**
@@ -192,22 +193,30 @@ abstract contract AccessControlCore {
192
193
  * @notice Updates set of permissions (role) for a given user,
193
194
  * taking into account sender's permissions.
194
195
  *
195
- * @dev Setting role to zero is equivalent to removing an all permissions
196
+ * @dev Setting role to zero is equivalent to removing all the permissions
196
197
  * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
197
198
  * copying senders' permissions (role) to the user
198
199
  * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
199
200
  *
201
+ * ╔════════════════════════════════════════════════════════════════════════╗
202
+ * ║ WARNING: RISK OF ACCIDENTAL SELF-REVOKE ║
203
+ * ╠════════════════════════════════════════════════════════════════════════╣
204
+ * ║ updateRole function is used to add, update, and delete permissions, ║
205
+ * ║ as well as to revoke and self-revoke all the permissions ║
206
+ * ║ ║
207
+ * ║ updateRole(msg.sender, 0) executed by a super admin themselves ║
208
+ * ║ revokes super admin permissions forever if there is no other super ║
209
+ * ║ admin set prior to updateRole(msg.sender, 0) call ║
210
+ * ╚════════════════════════════════════════════════════════════════════════╝
211
+ *
200
212
  * @param operator address of a user to alter permissions for,
201
213
  * or self address to alter global features of the smart contract
202
214
  * @param role bitmask representing a set of permissions to
203
215
  * enable/disable for a user specified
204
216
  */
205
- function updateRole(address operator, uint256 role) public {
206
- // caller must have a permission to update user roles
207
- _requireSenderInRole(ROLE_ACCESS_MANAGER);
208
-
209
- // evaluate the role and reassign it
210
- __setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
217
+ function updateRole(address operator, uint256 role) external {
218
+ // delegate to internal `_updateRole()`
219
+ _updateRole(operator, role);
211
220
  }
212
221
 
213
222
  /**
@@ -319,6 +328,28 @@ abstract contract AccessControlCore {
319
328
  return __hasRole(getRole(operator), required);
320
329
  }
321
330
 
331
+ /**
332
+ * @dev Updates set of permissions (role) for a given user,
333
+ * taking into account sender's permissions.
334
+ *
335
+ * @dev Setting role to zero is equivalent to removing all the permissions
336
+ * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
337
+ * copying senders' permissions (role) to the user
338
+ * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
339
+ *
340
+ * @param operator address of a user to alter permissions for,
341
+ * or self address to alter global features of the smart contract
342
+ * @param role bitmask representing a set of permissions to
343
+ * enable/disable for a user specified
344
+ */
345
+ function _updateRole(address operator, uint256 role) internal {
346
+ // caller must have a permission to update user roles
347
+ _requireSenderInRole(ROLE_ACCESS_MANAGER);
348
+
349
+ // evaluate the role and reassign it
350
+ __setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
351
+ }
352
+
322
353
  /**
323
354
  * @dev Sets the `assignedRole` role to the operator, logs both `requestedRole` and `actualRole`
324
355
  *
@@ -339,7 +370,7 @@ abstract contract AccessControlCore {
339
370
  userRoles[operator] = assignedRole;
340
371
 
341
372
  // fire an event
342
- emit RoleUpdated(operator, requestedRole, assignedRole);
373
+ emit RoleUpdated(msg.sender, operator, requestedRole, assignedRole);
343
374
  }
344
375
 
345
376
  /**
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity >=0.8.4;
2
+ pragma solidity >=0.8.20;
3
3
 
4
4
  import "./OwnableToAccessControlAdapter.sol";
5
5
 
@@ -39,7 +39,10 @@ contract AdapterFactory {
39
39
  * @param targetAddress OZ Ownable target address to bind OwnableToAccessControlAdapter to
40
40
  * @return address of the newly deployed OwnableToAccessControlAdapter contract
41
41
  */
42
- function deployNewOwnableToAccessControlAdapter(address targetAddress) public returns(address) {
42
+ function deployNewOwnableToAccessControlAdapter(address targetAddress) external returns(address) {
43
+ // verify the inputs
44
+ require(targetAddress != address(0), "zero address");
45
+ require(targetAddress.code.length != 0, "EOA");
43
46
  // verify sender is a target owner
44
47
  require(Ownable(targetAddress).owner() == msg.sender, "not an owner");
45
48
 
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  // breaking changes in .call() (0.5.0)
3
3
  // allow .call{}() (0.6.2)
4
- pragma solidity >=0.8.4;
4
+ pragma solidity >=0.8.20;
5
5
 
6
6
  import "./AccessControl.sol";
7
7
 
@@ -71,7 +71,7 @@ contract OwnableToAccessControlAdapter is AccessControlCore {
71
71
  *
72
72
  * @dev Target contract must transfer its ownership to the AccessControl Adapter
73
73
  */
74
- address public target;
74
+ address public immutable target;
75
75
 
76
76
  /**
77
77
  * @dev Access roles mapping stores the roles required to access the functions on the
@@ -95,16 +95,17 @@ contract OwnableToAccessControlAdapter is AccessControlCore {
95
95
  * @param selector selector of the function which corresponding access role was updated
96
96
  * @param role effective required role to execute the function defined by the selector
97
97
  */
98
- event AccessRoleUpdated(bytes4 selector, uint256 role);
98
+ event AccessRoleUpdated(bytes4 indexed selector, uint256 role);
99
99
 
100
100
  /**
101
101
  * @dev Logs function execution result on the target if the execution completed successfully
102
102
  *
103
103
  * @param selector selector of the function which was executed on the target contract
104
+ * @param roleRequired role that was required to execute the function requested
104
105
  * @param data full calldata payload passed to the target contract (includes the 4-bytes selector)
105
106
  * @param result execution response from the target contract
106
107
  */
107
- event ExecutionComplete(bytes4 selector, bytes data, bytes result);
108
+ event ExecutionComplete(bytes4 indexed selector, uint256 roleRequired, bytes data, bytes result);
108
109
 
109
110
  /**
110
111
  * @dev Deploys an AccessControl Adapter binding it to the target OZ Ownable contract,
@@ -116,6 +117,7 @@ contract OwnableToAccessControlAdapter is AccessControlCore {
116
117
  constructor(address _target, address _owner) AccessControlCore(_owner, 0) { // visibility modifier is required to be compilable with 0.6.x
117
118
  // verify the inputs
118
119
  require(_target != address(0), "zero address");
120
+ require(_target.code.length != 0, "EOA");
119
121
 
120
122
  // initialize internal contract state
121
123
  target = _target;
@@ -132,9 +134,9 @@ contract OwnableToAccessControlAdapter is AccessControlCore {
132
134
  * @param role role required to execute this function, or zero to disable
133
135
  * access to the specified function for everyone
134
136
  */
135
- function updateAccessRole(string memory signature, uint256 role) public {
136
- // delegate to `updateAccessRole(bytes4, uint256)`
137
- updateAccessRole(bytes4(keccak256(bytes(signature))), role);
137
+ function updateAccessRole(string memory signature, uint256 role) external {
138
+ // delegate to internal `_updateAccessRole(bytes4, uint256)`
139
+ __updateAccessRole(bytes4(keccak256(bytes(signature))), role);
138
140
  }
139
141
 
140
142
  /**
@@ -148,7 +150,23 @@ contract OwnableToAccessControlAdapter is AccessControlCore {
148
150
  * @param role role required to execute this function, or zero to disable
149
151
  * access to the specified function for everyone
150
152
  */
151
- function updateAccessRole(bytes4 selector, uint256 role) public {
153
+ function updateAccessRole(bytes4 selector, uint256 role) external {
154
+ // delegate to internal `_updateAccessRole(bytes4, uint256)`
155
+ __updateAccessRole(selector, role);
156
+ }
157
+
158
+ /**
159
+ * @dev Updates the access role required to execute the function defined by its selector
160
+ * on the target contract
161
+ *
162
+ * @dev More on function signatures and selectors: https://docs.soliditylang.org/en/develop/abi-spec.html
163
+ *
164
+ * @param selector function selector on the target contract, for example
165
+ * 0xf2fde38b selector corresponds to the "transferOwnership(address)" function
166
+ * @param role role required to execute this function, or zero to disable
167
+ * access to the specified function for everyone
168
+ */
169
+ function __updateAccessRole(bytes4 selector, uint256 role) private {
152
170
  // verify the access permission
153
171
  _requireSenderInRole(ROLE_ACCESS_ROLES_MANAGER);
154
172
 
@@ -175,34 +193,25 @@ contract OwnableToAccessControlAdapter is AccessControlCore {
175
193
  */
176
194
  function execute(bytes memory data) public payable returns(bytes memory) {
177
195
  // extract the selector (first 4 bytes as bytes4) using assembly
178
- bytes4 selector;
179
- assembly {
180
- // load the first word after the length field
181
- selector := mload(add(data, 32))
182
- }
196
+ bytes4 selector = data.length == 0? bytes4(0x00000000): __extractSelector(data);
183
197
 
184
- // zero data length means we're trying to execute the receive() function on
185
- // the target and supply some ether to the target; in this case we don't need a security check
186
- // if the data is present, we're executing some real function and must do a security check
187
- if(data.length != 0) {
188
- // determine the role required to access the function
189
- uint256 roleRequired = accessRoles[selector];
198
+ // determine the role required to access the function
199
+ uint256 roleRequired = accessRoles[selector];
190
200
 
191
- // verify function access role was already set
192
- require(roleRequired != 0, "access role not set");
201
+ // verify function access role was already set
202
+ require(roleRequired != 0, "access role not set");
193
203
 
194
- // verify the access permission
195
- _requireSenderInRole(roleRequired);
196
- }
204
+ // verify the access permission
205
+ _requireSenderInRole(roleRequired);
197
206
 
198
207
  // execute the call on the target
199
208
  (bool success, bytes memory result) = address(target).call{value: msg.value}(data);
200
209
 
201
210
  // verify the execution completed successfully
202
- require(success, "execution failed");
211
+ __requireSuccessfulCall(success, result);
203
212
 
204
213
  // emit an event
205
- emit ExecutionComplete(selector, data, result);
214
+ emit ExecutionComplete(selector, roleRequired, data, result);
206
215
 
207
216
  // return the result
208
217
  return result;
@@ -230,4 +239,44 @@ contract OwnableToAccessControlAdapter is AccessControlCore {
230
239
  // delegate to `execute(bytes)`
231
240
  execute(msg.data);
232
241
  }
242
+
243
+ /// @dev Extracts first 4 bytes from the input, throwing if input is less than 4 bytes long
244
+ function __extractSelector(bytes memory data) private pure returns(bytes4) {
245
+ // verify data has at least 4 bytes to read
246
+ require(data.length >= 4, "bad selector");
247
+ // extract the selector (first 4 bytes as bytes4) using assembly
248
+ bytes4 selector;
249
+ assembly {
250
+ // load the first word after the length field
251
+ selector := mload(add(data, 32))
252
+ }
253
+ // return whatever we've loaded from the memory
254
+ return selector;
255
+ }
256
+
257
+ /// @dev Mimics the require(success, string(returndata))
258
+ function __requireSuccessfulCall(bool success, bytes memory returndata) private pure {
259
+ // if operation was not successful
260
+ if(!success) {
261
+ // revert, trying to deliver original error message from the low-level call,
262
+ // and falling back to "execution failed" if low-level call returned no message
263
+ __revert(returndata, "execution failed");
264
+ }
265
+ }
266
+
267
+ /// @dev Copied as is from OZ 4.9.6 @openzeppelin/contracts/utils/Address.sol::_revert(bytes,string)
268
+ function __revert(bytes memory returndata, string memory errorMessage) private pure {
269
+ // Look for revert reason and bubble it up if present
270
+ if(returndata.length > 0) {
271
+ // The easiest way to bubble the revert reason is using memory via assembly
272
+ /// @solidity memory-safe-assembly
273
+ assembly {
274
+ let returndata_size := mload(returndata)
275
+ revert(add(32, returndata), returndata_size)
276
+ }
277
+ }
278
+ else {
279
+ revert(errorMessage);
280
+ }
281
+ }
233
282
  }
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.4;
3
+
4
+ contract ErrorHelper {
5
+ address public owner;
6
+
7
+ function transferOwnership(address _owner) public {
8
+ owner = _owner;
9
+ }
10
+
11
+ function throwError(string memory message) public pure {
12
+ require(false, message);
13
+ }
14
+ }
@@ -237,4 +237,5 @@ See also: [Gitlab Testing Standards](https://docs.gitlab.com/ee/development/test
237
237
 
238
238
  Prepared by Basil Gorin
239
239
 
240
- (c) 2017–2024 Basil Gorin
240
+ (c) 2017–2024 Basil Gorin
241
+ (c) 2024–2025 Lazy So\[u\]l
package/hardhat.config.js CHANGED
@@ -333,7 +333,7 @@ module.exports = {
333
333
  // https://hardhat.org/guides/compile-contracts.html
334
334
  compilers: [
335
335
  {
336
- version: "0.8.21",
336
+ version: "0.8.28",
337
337
  settings: {
338
338
  optimizer: {
339
339
  enabled: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazy-sol/access-control",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Enable the modular plug and play (PnP) architecture for your dapp by incorporating the role-based access control (RBAC) into the smart contracts",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,16 +22,18 @@
22
22
  "author": "Basil Gorin",
23
23
  "license": "MIT",
24
24
  "devDependencies": {
25
- "@lazy-sol/a-missing-gem": "^1.0.11",
25
+ "@lazy-sol/a-missing-gem": "^1.0.12",
26
26
  "@lazy-sol/zeppelin-test-helpers": "^1.0.5",
27
27
  "@nomiclabs/hardhat-truffle5": "^2.0.7",
28
- "hardhat": "^2.22.13",
28
+ "hardhat": "^2.25.0",
29
29
  "hardhat-deploy": "^0.11.45",
30
30
  "hardhat-gas-reporter": "^1.0.10",
31
- "solidity-coverage": "^0.8.13"
31
+ "solidity-coverage": "^0.8.16"
32
32
  },
33
33
  "overrides": {
34
34
  "axios": ">=1.7.5",
35
+ "cookie": ">=0.7.0",
36
+ "elliptic": "^6.6.0",
35
37
  "micromatch": "^4.0.8",
36
38
  "tar": "^6.2.1",
37
39
  "tough-cookie": "^4.1.3",
@@ -0,0 +1,12 @@
1
+ Report AccessControl
2
+ add Minor-1 | Insufficient Event Logging in `RoleUpdated` and shift other Minor numbering by one:
3
+ Minor-2 | No Check for `role != 0` in `updateRole`
4
+ Minor-3 | State Variables Could Be Declared immutable (OwnableToAccessControlAdapter)
5
+
6
+ Report AccessControlUpgradable
7
+ switch Notes-1. Gas Optimizations and Notes-2. Use Latest Solidity Version (^0.8.26), use the same naming as in AccessControl report:
8
+ Notes-1 | Version Constraints with Known Issues (may keep the original Use Latest Solidity Version (^0.8.26) name but then need to change to the same in AccessControl report)
9
+ Notes-2 | Visibility Optimization for Gas Savings
10
+ add Minor-2 | No Check for `role != 0` in `updateRole`
11
+ add Notes-3 | Potential Improvement for `FULL_PRIVILEGES_MASK` Assignment and shift Notes-3. Test Coverage by one: Notes-4. Test Coverage
12
+
@@ -21,7 +21,7 @@ const {
21
21
  const {
22
22
  deploy_usdt,
23
23
  deploy_adapter_factory,
24
- factory_deploy_ownable_to_ac_adapter,
24
+ factory_deploy_ownable_to_ac_adapter_pure,
25
25
  } = require("./include/deployment_routines");
26
26
 
27
27
  // run AdapterFactory tests
@@ -39,13 +39,19 @@ contract("AdapterFactory tests", function(accounts) {
39
39
  factory = await deploy_adapter_factory();
40
40
  });
41
41
 
42
+ it("adapter deployment fails if target address is zero address", async function() {
43
+ await expectRevert(factory_deploy_ownable_to_ac_adapter_pure(a1, factory, ZERO_ADDRESS), "zero address");
44
+ });
45
+ it("adapter deployment fails if target address is EOA", async function() {
46
+ await expectRevert(factory_deploy_ownable_to_ac_adapter_pure(a1, factory, a2), "EOA");
47
+ });
42
48
  it("adapter deployment fails if executed not by the ownable owner", async function() {
43
- await expectRevert(factory_deploy_ownable_to_ac_adapter(a2, factory, usdt), "not an owner");
49
+ await expectRevert(factory_deploy_ownable_to_ac_adapter_pure(a2, factory, usdt), "not an owner");
44
50
  });
45
51
  describe("adapter deployment succeeds if executed by the ownable owner", async function() {
46
52
  let adapter;
47
53
  beforeEach(async function() {
48
- ({adapter} = await factory_deploy_ownable_to_ac_adapter(a1, factory, usdt));
54
+ adapter = await factory_deploy_ownable_to_ac_adapter_pure(a1, factory, usdt);
49
55
  });
50
56
  it("adapter's target is set correctly", async function() {
51
57
  expect(await adapter.target()).to.be.equal(usdt.address);
@@ -6,13 +6,21 @@
6
6
  * @returns USDT ERC20 instance
7
7
  */
8
8
  async function deploy_usdt(a0) {
9
- // smart contracts required
10
9
  const USDTContract = artifacts.require("TetherToken");
11
-
12
- // deploy the token and return the reference
13
10
  return await USDTContract.new(0, "Tether USD", "USDT", 6, {from: a0});
14
11
  }
15
12
 
13
+ /**
14
+ * Deploys ErrorHelper, which helps to throw arbitrary error string, used in tests
15
+ *
16
+ * @param a0 deployer account
17
+ * @return ErrorHelper instance
18
+ */
19
+ async function deploy_error_helper(a0) {
20
+ const ErrorHelper = artifacts.require("ErrorHelper");
21
+ return await ErrorHelper.new({from: a0});
22
+ }
23
+
16
24
  /**
17
25
  * Deploys AccessControl contract
18
26
  *
@@ -22,10 +30,7 @@ async function deploy_usdt(a0) {
22
30
  * @returns AccessControl instance
23
31
  */
24
32
  async function deploy_access_control(a0, owner = a0, features = 0) {
25
- // deploy AccessControlMock
26
33
  const AccessControlMock = artifacts.require("AccessControlMock");
27
-
28
- // deploy and return the instance
29
34
  return await AccessControlMock.new(owner, features, {from: a0});
30
35
  }
31
36
 
@@ -67,9 +72,7 @@ async function deploy_ownable_to_ac_adapter(a0, target) {
67
72
  * @returns OwnableToAccessControlAdapter instance
68
73
  */
69
74
  async function deploy_no_deps_ownable_to_ac_adapter(a0, target) {
70
- // artifacts in use
71
75
  const OwnableToAccessControlAdapter = artifacts.require("OwnableToAccessControlAdapter");
72
- // deploy and return the deployd instance
73
76
  return await OwnableToAccessControlAdapter.new(target.address || target, a0, {from: a0});
74
77
  }
75
78
 
@@ -80,15 +83,14 @@ async function deploy_no_deps_ownable_to_ac_adapter(a0, target) {
80
83
  * @returns AdapterFactory instance
81
84
  */
82
85
  async function deploy_adapter_factory(a0) {
83
- // artifacts in use
84
86
  const AdapterFactory = artifacts.require("AdapterFactory");
85
- // deploy and return the deployd instance
86
87
  return await AdapterFactory.new(a0? {from: a0}: undefined);
87
88
  }
88
89
 
89
90
  /**
90
91
  * Deploys OwnableToAccessControlAdapter via the AdapterFactory
91
92
  * Deploys the AdapterFactory and target Ownable if required
93
+ * Transfers ownership from the target to the adapter
92
94
  *
93
95
  * @param a0 deployer address, target owner, required
94
96
  * @param factory AdapterFactory instance or address, optional
@@ -113,15 +115,7 @@ async function factory_deploy_ownable_to_ac_adapter(a0, factory, target) {
113
115
  }
114
116
 
115
117
  // deploy the adapter via the AdapterFactory
116
- const receipt = await factory.deployNewOwnableToAccessControlAdapter(target.address, {from: a0});
117
- const {
118
- adapterAddress,
119
- ownableTargetAddress,
120
- } = receipt.logs.find(log => log.event === "NewOwnableToAccessControlAdapterDeployed").args;
121
-
122
- // connect to the adapter
123
- const OwnableToAccessControlAdapter = artifacts.require("OwnableToAccessControlAdapter");
124
- const adapter = await OwnableToAccessControlAdapter.at(adapterAddress);
118
+ const adapter = await factory_deploy_ownable_to_ac_adapter_pure(a0, factory, target);
125
119
 
126
120
  // transfer ownership to the adapter
127
121
  await target.transferOwnership(adapter.address, {from: a0});
@@ -130,12 +124,35 @@ async function factory_deploy_ownable_to_ac_adapter(a0, factory, target) {
130
124
  return {factory, target, adapter};
131
125
  }
132
126
 
127
+ /**
128
+ * Deploys OwnableToAccessControlAdapter via the AdapterFactory
129
+ *
130
+ * @param a0 deployer address, target owner, required
131
+ * @param factory AdapterFactory instance, required
132
+ * @param target Ownable instance or address, required
133
+ * @returns OwnableToAccessControlAdapter instance
134
+ */
135
+ async function factory_deploy_ownable_to_ac_adapter_pure(a0, factory, target) {
136
+ // deploy the adapter via the AdapterFactory
137
+ const receipt = await factory.deployNewOwnableToAccessControlAdapter(target.address || target, {from: a0});
138
+ const {
139
+ adapterAddress,
140
+ ownableTargetAddress,
141
+ } = receipt.logs.find(log => log.event === "NewOwnableToAccessControlAdapterDeployed").args;
142
+
143
+ // connect to the adapter and return the result
144
+ const OwnableToAccessControlAdapter = artifacts.require("OwnableToAccessControlAdapter");
145
+ return await OwnableToAccessControlAdapter.at(adapterAddress);
146
+ }
147
+
133
148
  // export public deployment API
134
149
  module.exports = {
135
150
  deploy_usdt,
151
+ deploy_error_helper,
136
152
  deploy_access_control,
137
153
  deploy_no_deps_ownable_to_ac_adapter,
138
154
  deploy_ownable_to_ac_adapter,
139
155
  deploy_adapter_factory,
140
156
  factory_deploy_ownable_to_ac_adapter,
157
+ factory_deploy_ownable_to_ac_adapter_pure,
141
158
  }
@@ -62,6 +62,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
62
62
  if(owner !== ZERO_ADDRESS) {
63
63
  it('"RoleUpdated(owner)" event is emitted correctly', async function() {
64
64
  await expectEvent.inConstruction(access_control, "RoleUpdated", {
65
+ by: a0,
65
66
  operator: owner,
66
67
  requested: FULL_PRIVILEGES_MASK,
67
68
  assigned: FULL_PRIVILEGES_MASK,
@@ -70,6 +71,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
70
71
  }
71
72
  it('"RoleUpdated(this)" event is emitted correctly', async function() {
72
73
  await expectEvent.inConstruction(access_control, "RoleUpdated", {
74
+ by: a0,
73
75
  operator: access_control.address,
74
76
  requested: features,
75
77
  assigned: features,
@@ -134,6 +136,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
134
136
  });
135
137
  it('"RoleUpdated" event', async function() {
136
138
  expectEvent(receipt, "RoleUpdated", {
139
+ by,
137
140
  operator: to_fn(to),
138
141
  requested: set,
139
142
  assigned: set,
@@ -157,6 +160,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
157
160
  });
158
161
  it('"RoleUpdated" event', async function() {
159
162
  expectEvent(receipt, "RoleUpdated", {
163
+ by,
160
164
  operator: to_fn(to),
161
165
  requested: not(remove),
162
166
  assigned: not(remove),
@@ -182,6 +186,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
182
186
  });
183
187
  it('"RoleUpdated" event', async function() {
184
188
  expectEvent(receipt, "RoleUpdated", {
189
+ by,
185
190
  operator: to_fn(to),
186
191
  requested: set,
187
192
  assigned: "0",
@@ -206,6 +211,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
206
211
  });
207
212
  it('"RoleUpdated" event', async function() {
208
213
  expectEvent(receipt, "RoleUpdated", {
214
+ by,
209
215
  operator: to_fn(to),
210
216
  requested: not(remove),
211
217
  assigned: MAX_UINT256,
@@ -237,6 +243,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
237
243
  });
238
244
  it('"RoleUpdated" event', async function() {
239
245
  expectEvent(receipt, "RoleUpdated", {
246
+ by,
240
247
  operator: to_fn(to),
241
248
  requested: set,
242
249
  assigned: role.and(set),
@@ -261,6 +268,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
261
268
  });
262
269
  it('"RoleUpdated" event', async function() {
263
270
  expectEvent(receipt, "RoleUpdated", {
271
+ by,
264
272
  operator: to_fn(to),
265
273
  requested: not(remove),
266
274
  assigned: not(role.and(remove)),
@@ -17,8 +17,12 @@ const {
17
17
  MAX_UINT256,
18
18
  } = constants;
19
19
 
20
+ // BN constants and utilities
21
+ const {random_bn256} = require("@lazy-sol/a-missing-gem");
22
+
20
23
  // deployment routines in use
21
24
  const {
25
+ deploy_error_helper,
22
26
  deploy_ownable_to_ac_adapter,
23
27
  deploy_no_deps_ownable_to_ac_adapter,
24
28
  } = require("./include/deployment_routines");
@@ -32,16 +36,27 @@ contract("OwnableToAccessControlAdapter tests", function(accounts) {
32
36
  // a1, a2,... – working accounts to perform tests on
33
37
  const [A0, a0, H0, a1, a2, a3] = accounts;
34
38
 
35
- it("Adapter won't deploy targeting to a zero address", async function() {
39
+ it("adapter deployment fails if target is a zero address", async function() {
36
40
  await expectRevert(deploy_no_deps_ownable_to_ac_adapter(a0, ZERO_ADDRESS), "zero address");
37
41
  });
42
+ it("adapter deployment fails if target is an EOA", async function() {
43
+ await expectRevert(deploy_no_deps_ownable_to_ac_adapter(a0, a1), "EOA");
44
+ });
38
45
  describe("after the Adapter is deployed and target Ownable ownership transferred to the Adapter", function() {
39
46
  let target, adapter;
40
47
  beforeEach(async function() {
41
48
  ({target, adapter} = await deploy_ownable_to_ac_adapter(a0));
42
49
  });
43
50
 
51
+ it("it is impossible to send ether before empty selector access role is configured", async function() {
52
+ await expectRevert(web3.eth.sendTransaction({
53
+ from: a0,
54
+ to: adapter.address,
55
+ value: 1_000_000_000, // 1 gwei
56
+ }), "access role not set");
57
+ });
44
58
  it("it is impossible to send ether to the non-payable target contract via the adapter", async function() {
59
+ await adapter.methods["updateAccessRole(bytes4,uint256)"]("0x00000000", 1, {from: a0});
45
60
  await expectRevert(web3.eth.sendTransaction({
46
61
  from: a0,
47
62
  to: adapter.address,
@@ -55,15 +70,16 @@ contract("OwnableToAccessControlAdapter tests", function(accounts) {
55
70
  data: target.contract.methods.transferOwnership(a2).encodeABI(),
56
71
  }), "access role not set");
57
72
  });
58
- describe("once transferOwnership function access control is configured", function() {
73
+ describe("when transferOwnership function access control is configured", function() {
59
74
  const ROLE_OWNERSHIP_MANAGER = 0x00010000;
75
+ const fn_selector = web3.eth.abi.encodeFunctionSignature("transferOwnership(address)");
60
76
  let receipt;
61
77
  beforeEach(async function() {
62
- receipt = await adapter.updateAccessRole("transferOwnership(address)", ROLE_OWNERSHIP_MANAGER, {from: a0});
78
+ receipt = await adapter.methods["updateAccessRole(string,uint256)"]("transferOwnership(address)", ROLE_OWNERSHIP_MANAGER, {from: a0});
63
79
  });
64
80
  it('"AccessRoleUpdated" event is emitted', async function() {
65
81
  expectEvent(receipt, "AccessRoleUpdated", {
66
- selector: web3.eth.abi.encodeFunctionSignature("transferOwnership(address)"),
82
+ selector: web3.utils.padRight(fn_selector, 64),
67
83
  role: "" + ROLE_OWNERSHIP_MANAGER,
68
84
  });
69
85
  });
@@ -78,23 +94,95 @@ contract("OwnableToAccessControlAdapter tests", function(accounts) {
78
94
  beforeEach(async function() {
79
95
  await adapter.updateRole(a1, ROLE_OWNERSHIP_MANAGER, {from: a0});
80
96
  });
81
- it("execution of the transferOwnership function succeeds", async function() {
82
- await web3.eth.sendTransaction({
97
+ it("execution of the transferOwnership non-payable function fails if ether is supplied", async function() {
98
+ await expectRevert(web3.eth.sendTransaction({
83
99
  from: a1,
84
100
  to: adapter.address,
85
101
  data: target.contract.methods.transferOwnership(a2).encodeABI(),
86
- });
87
- expect(await target.owner(), "wrong owner after ownership transfer").to.equal(a2);
102
+ value: 1_000_000_000, // 1 gwei
103
+ }), "execution failed");
88
104
  });
89
- it("execution of the transferOwnership non-payable function fails if ether is supplied", async function() {
105
+ it("execution of the transferOwnership fails if function selector is corrupted", async function() {
90
106
  await expectRevert(web3.eth.sendTransaction({
91
107
  from: a1,
92
108
  to: adapter.address,
93
- data: target.contract.methods.transferOwnership(a2).encodeABI(),
109
+ data: "0x112233",
94
110
  value: 1_000_000_000, // 1 gwei
95
- }), "execution failed");
111
+ }), "bad selector");
112
+ });
113
+ describe("execution of the transferOwnership function succeeds otherwise", function() {
114
+ let receipt, data;
115
+ beforeEach(async function() {
116
+ data = target.contract.methods.transferOwnership(a2).encodeABI();
117
+ receipt = await web3.eth.sendTransaction({
118
+ from: a1,
119
+ to: adapter.address,
120
+ data: data,
121
+ });
122
+ });
123
+ it('"ExecutionComplete" event is emitted', async function() {
124
+ await expectEvent.inTransaction(receipt.transactionHash, adapter, "ExecutionComplete", {
125
+ selector: web3.utils.padRight(fn_selector, 64),
126
+ roleRequired: ROLE_OWNERSHIP_MANAGER,
127
+ data: data,
128
+ result: null,
129
+ });
130
+ });
131
+ it("owner gets transferred correctly", async function() {
132
+ expect(await target.owner()).to.equal(a2);
133
+ });
96
134
  });
97
135
  });
98
136
  });
137
+ describe("configuring access to the underlying functions (updateAccessRole)", function() {
138
+ const fn_signature = "1234";
139
+ const fn_selector = web3.eth.abi.encodeFunctionSignature(fn_signature);
140
+ const access_permission = random_bn256();
141
+ describe("when setting the access with updateAccessRole(string, uint256)", function() {
142
+ let receipt;
143
+ beforeEach(async function() {
144
+ receipt = await adapter.methods["updateAccessRole(string,uint256)"](fn_signature, access_permission, {from: a0});
145
+ });
146
+ it('"AccessRoleUpdated" event is emitted', async function() {
147
+ expectEvent(receipt, "AccessRoleUpdated", {
148
+ selector: web3.utils.padRight(fn_selector, 64),
149
+ role: "" + access_permission,
150
+ });
151
+ });
152
+ it("access permission is updated", async function() {
153
+ expect(await adapter.accessRoles(fn_selector)).to.be.bignumber.that.equals(access_permission);
154
+ });
155
+ });
156
+ describe("when setting the access with updateAccessRole(bytes4, uint256)", function() {
157
+ let receipt;
158
+ beforeEach(async function() {
159
+ receipt = await adapter.methods["updateAccessRole(bytes4,uint256)"](fn_selector, access_permission, {from: a0});
160
+ });
161
+ it('"AccessRoleUpdated" event is emitted', async function() {
162
+ expectEvent(receipt, "AccessRoleUpdated", {
163
+ selector: web3.utils.padRight(fn_selector, 64),
164
+ role: "" + access_permission,
165
+ });
166
+ });
167
+ it("access permission is updated", async function() {
168
+ expect(await adapter.accessRoles(fn_selector)).to.be.bignumber.that.equals(access_permission);
169
+ });
170
+ });
171
+ });
172
+ });
173
+ describe("after the Adapter is deployed, targeting to the ErrorHelper contract (custom execution error check)", function() {
174
+ let target, adapter;
175
+ beforeEach(async function() {
176
+ target = await deploy_error_helper(a0);
177
+ adapter = await deploy_no_deps_ownable_to_ac_adapter(a0, target);
178
+ await adapter.methods["updateAccessRole(string,uint256)"]("throwError(string)", 1, {from: a0});
179
+ await adapter.updateRole(a1, 1, {from: a0});
180
+ });
181
+
182
+ it("custom error string gets propagated from the target through the adapter", async function() {
183
+ const message = "Hello, World!";
184
+ const data = target.contract.methods.throwError(message).encodeABI();
185
+ await expectRevert(adapter.execute(data, {from: a1}), message);
186
+ });
99
187
  });
100
188
  });
@@ -46,12 +46,14 @@ contract("OwnableToAccessControlAdapter: RBAC tests", function(accounts) {
46
46
  it("it is impossible to configure the access role from an unauthorized account", async function() {
47
47
  const operator = a1;
48
48
  await adapter.updateRole(operator, not(ROLE_ACCESS_ROLES_MANAGER), {from: a0});
49
- await expectRevert(adapter.updateAccessRole("1", 1, {from: operator}), "AccessDenied()");
49
+ await expectRevert(adapter.methods["updateAccessRole(string,uint256)"]("1", 1, {from: operator}), "AccessDenied()");
50
+ await expectRevert(adapter.methods["updateAccessRole(bytes4,uint256)"]("0x12345678", 1, {from: operator}), "AccessDenied()");
50
51
  });
51
52
  it("it is possible to configure the access role from the authorized account", async function() {
52
53
  const operator = a1;
53
54
  await adapter.updateRole(operator, ROLE_ACCESS_ROLES_MANAGER, {from: a0});
54
- adapter.updateAccessRole("1", 1, {from: operator});
55
+ adapter.methods["updateAccessRole(string,uint256)"]("1", 1, {from: operator});
56
+ adapter.methods["updateAccessRole(bytes4,uint256)"]("0x12345678", 1, {from: operator});
55
57
  });
56
58
  });
57
59
  });
package/ui.html CHANGED
@@ -44,7 +44,7 @@
44
44
  </div>
45
45
  </div>
46
46
  </fieldset>
47
- <div style="position: fixed; left: 0; bottom: 0; margin: 0.1em 0.2em;">&copy; 2024 <a href="https://github.com/lazy-sol/">Lazy So[u]l</a></div>
47
+ <div style="position: fixed; left: 0; bottom: 0; margin: 0.1em 0.2em;">&copy; 2024–2025 <a href="https://github.com/lazy-sol/">Lazy So[u]l</a></div>
48
48
  <div style="position: fixed; right: 0; bottom: 0; margin: 0.1em 0.2em; font-family: monospace;">
49
49
  [<a href="https://lazy-sol.github.io/advanced-erc20/ui.html">Advanced ERC20</a>]
50
50
  [<a href="https://lazy-sol.github.io/tiny-erc721/ui.html">Tiny ERC721</a>]
@@ -1,285 +0,0 @@
1
- {
2
- "_format": "hh-sol-artifact-1",
3
- "contractName": "OwnableToAccessControlAdapter",
4
- "sourceName": "contracts/OwnableToAccessControlAdapter.sol",
5
- "abi": [
6
- {
7
- "inputs": [
8
- {
9
- "internalType": "address",
10
- "name": "_target",
11
- "type": "address"
12
- },
13
- {
14
- "internalType": "address",
15
- "name": "_owner",
16
- "type": "address"
17
- }
18
- ],
19
- "stateMutability": "nonpayable",
20
- "type": "constructor"
21
- },
22
- {
23
- "inputs": [],
24
- "name": "AccessDenied",
25
- "type": "error"
26
- },
27
- {
28
- "anonymous": false,
29
- "inputs": [
30
- {
31
- "indexed": false,
32
- "internalType": "bytes4",
33
- "name": "selector",
34
- "type": "bytes4"
35
- },
36
- {
37
- "indexed": false,
38
- "internalType": "uint256",
39
- "name": "role",
40
- "type": "uint256"
41
- }
42
- ],
43
- "name": "AccessRoleUpdated",
44
- "type": "event"
45
- },
46
- {
47
- "anonymous": false,
48
- "inputs": [
49
- {
50
- "indexed": false,
51
- "internalType": "bytes4",
52
- "name": "selector",
53
- "type": "bytes4"
54
- },
55
- {
56
- "indexed": false,
57
- "internalType": "bytes",
58
- "name": "data",
59
- "type": "bytes"
60
- },
61
- {
62
- "indexed": false,
63
- "internalType": "bytes",
64
- "name": "result",
65
- "type": "bytes"
66
- }
67
- ],
68
- "name": "ExecutionComplete",
69
- "type": "event"
70
- },
71
- {
72
- "anonymous": false,
73
- "inputs": [
74
- {
75
- "indexed": true,
76
- "internalType": "address",
77
- "name": "operator",
78
- "type": "address"
79
- },
80
- {
81
- "indexed": false,
82
- "internalType": "uint256",
83
- "name": "requested",
84
- "type": "uint256"
85
- },
86
- {
87
- "indexed": false,
88
- "internalType": "uint256",
89
- "name": "assigned",
90
- "type": "uint256"
91
- }
92
- ],
93
- "name": "RoleUpdated",
94
- "type": "event"
95
- },
96
- {
97
- "stateMutability": "payable",
98
- "type": "fallback"
99
- },
100
- {
101
- "inputs": [],
102
- "name": "ROLE_ACCESS_MANAGER",
103
- "outputs": [
104
- {
105
- "internalType": "uint256",
106
- "name": "",
107
- "type": "uint256"
108
- }
109
- ],
110
- "stateMutability": "view",
111
- "type": "function"
112
- },
113
- {
114
- "inputs": [],
115
- "name": "ROLE_ACCESS_ROLES_MANAGER",
116
- "outputs": [
117
- {
118
- "internalType": "uint256",
119
- "name": "",
120
- "type": "uint256"
121
- }
122
- ],
123
- "stateMutability": "view",
124
- "type": "function"
125
- },
126
- {
127
- "inputs": [
128
- {
129
- "internalType": "bytes4",
130
- "name": "",
131
- "type": "bytes4"
132
- }
133
- ],
134
- "name": "accessRoles",
135
- "outputs": [
136
- {
137
- "internalType": "uint256",
138
- "name": "",
139
- "type": "uint256"
140
- }
141
- ],
142
- "stateMutability": "view",
143
- "type": "function"
144
- },
145
- {
146
- "inputs": [
147
- {
148
- "internalType": "bytes",
149
- "name": "data",
150
- "type": "bytes"
151
- }
152
- ],
153
- "name": "execute",
154
- "outputs": [
155
- {
156
- "internalType": "bytes",
157
- "name": "",
158
- "type": "bytes"
159
- }
160
- ],
161
- "stateMutability": "payable",
162
- "type": "function"
163
- },
164
- {
165
- "inputs": [],
166
- "name": "features",
167
- "outputs": [
168
- {
169
- "internalType": "uint256",
170
- "name": "",
171
- "type": "uint256"
172
- }
173
- ],
174
- "stateMutability": "view",
175
- "type": "function"
176
- },
177
- {
178
- "inputs": [
179
- {
180
- "internalType": "address",
181
- "name": "operator",
182
- "type": "address"
183
- }
184
- ],
185
- "name": "getRole",
186
- "outputs": [
187
- {
188
- "internalType": "uint256",
189
- "name": "",
190
- "type": "uint256"
191
- }
192
- ],
193
- "stateMutability": "view",
194
- "type": "function"
195
- },
196
- {
197
- "inputs": [],
198
- "name": "target",
199
- "outputs": [
200
- {
201
- "internalType": "address",
202
- "name": "",
203
- "type": "address"
204
- }
205
- ],
206
- "stateMutability": "view",
207
- "type": "function"
208
- },
209
- {
210
- "inputs": [
211
- {
212
- "internalType": "bytes4",
213
- "name": "selector",
214
- "type": "bytes4"
215
- },
216
- {
217
- "internalType": "uint256",
218
- "name": "role",
219
- "type": "uint256"
220
- }
221
- ],
222
- "name": "updateAccessRole",
223
- "outputs": [],
224
- "stateMutability": "nonpayable",
225
- "type": "function"
226
- },
227
- {
228
- "inputs": [
229
- {
230
- "internalType": "string",
231
- "name": "signature",
232
- "type": "string"
233
- },
234
- {
235
- "internalType": "uint256",
236
- "name": "role",
237
- "type": "uint256"
238
- }
239
- ],
240
- "name": "updateAccessRole",
241
- "outputs": [],
242
- "stateMutability": "nonpayable",
243
- "type": "function"
244
- },
245
- {
246
- "inputs": [
247
- {
248
- "internalType": "uint256",
249
- "name": "_mask",
250
- "type": "uint256"
251
- }
252
- ],
253
- "name": "updateFeatures",
254
- "outputs": [],
255
- "stateMutability": "nonpayable",
256
- "type": "function"
257
- },
258
- {
259
- "inputs": [
260
- {
261
- "internalType": "address",
262
- "name": "operator",
263
- "type": "address"
264
- },
265
- {
266
- "internalType": "uint256",
267
- "name": "role",
268
- "type": "uint256"
269
- }
270
- ],
271
- "name": "updateRole",
272
- "outputs": [],
273
- "stateMutability": "nonpayable",
274
- "type": "function"
275
- },
276
- {
277
- "stateMutability": "payable",
278
- "type": "receive"
279
- }
280
- ],
281
- "bytecode": "0x608060405234801561001057600080fd5b50604051610a32380380610a3283398101604081905261002f9161013e565b8060006001600160a01b0382161561004e5761004e82600019806100ca565b6100593082806100ca565b50506001600160a01b0382166100a45760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b604482015260640160405180910390fd5b50600180546001600160a01b0319166001600160a01b0392909216919091179055610171565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b80516001600160a01b038116811461013957600080fd5b919050565b6000806040838503121561015157600080fd5b61015a83610122565b915061016860208401610122565b90509250929050565b6108b2806101806000396000f3fe6080604052600436106100a05760003560e01c8063491d261111610064578063491d2611146101d35780635defb40d146101f3578063ae5b102e14610213578063ae682e2e14610233578063d4b839921461024b578063d5bb7f6714610283576100bf565b806309c5eabe146100ff5780630e82fe25146101285780632b5214161461016357806334e48c9e14610185578063442767331461019d576100bf565b366100bf576100bd604051806020016040528060008152506102a3565b005b6100bd6000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506102a392505050565b61011261010d36600461064c565b6102a3565b60405161011f91906106ed565b60405180910390f35b34801561013457600080fd5b5061015561014336600461071d565b60026020526000908152604090205481565b60405190815260200161011f565b34801561016f57600080fd5b5030600090815260208190526040902054610155565b34801561019157600080fd5b50610155600160fd1b81565b3480156101a957600080fd5b506101556101b836600461074f565b6001600160a01b031660009081526020819052604090205490565b3480156101df57600080fd5b506100bd6101ee36600461076a565b610411565b3480156101ff57600080fd5b506100bd61020e366004610794565b610477565b34801561021f57600080fd5b506100bd61022e3660046107ed565b61048c565b34801561023f57600080fd5b50610155600160ff1b81565b34801561025757600080fd5b5060015461026b906001600160a01b031681565b6040516001600160a01b03909116815260200161011f565b34801561028f57600080fd5b506100bd61029e366004610809565b6104f5565b602081015181516060919015610323576001600160e01b03198116600090815260026020526040812054908190036103185760405162461bcd60e51b81526020600482015260136024820152721858d8d95cdcc81c9bdb19481b9bdd081cd95d606a1b60448201526064015b60405180910390fd5b61032181610502565b505b60015460405160009182916001600160a01b03909116903490610347908890610822565b60006040518083038185875af1925050503d8060008114610384576040519150601f19603f3d011682016040523d82523d6000602084013e610389565b606091505b5091509150816103ce5760405162461bcd60e51b815260206004820152601060248201526f195e1958dd5d1a5bdb8819985a5b195960821b604482015260640161030f565b7f57a62eca76fc623c92f161d2a4b851851ece707135ce2af1eec256d660571b6d8386836040516104019392919061083e565b60405180910390a1949350505050565b61041e600160fd1b610502565b6001600160e01b03198216600081815260026020908152604091829020849055815192835282018390527fdb8ed917742b49e83acd1322bcaa8f18b1e5f78a70784c43ea14db7ab50e628d910160405180910390a15050565b610488828051906020012082610411565b5050565b610499600160ff1b610502565b61048882826104f0336104c1876001600160a01b031660009081526020819052604090205490565b6001600160a01b0391909116600090815260208190526040902054600019808818821618908716919091171690565b610513565b6104ff308261048c565b50565b6104ff61050e8261056b565b61057d565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b6000610577338361059b565b92915050565b806104ff57604051634ca8886760e01b815260040160405180910390fd5b6001600160a01b038216600090815260208190526040812054821682145b9392505050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156105f1576105f16105c0565b604051601f8501601f19908116603f01168101908282118183101715610619576106196105c0565b8160405280935085815286868601111561063257600080fd5b858560208301376000602087830101525050509392505050565b60006020828403121561065e57600080fd5b813567ffffffffffffffff81111561067557600080fd5b8201601f8101841361068657600080fd5b610695848235602084016105d6565b949350505050565b60005b838110156106b85781810151838201526020016106a0565b50506000910152565b600081518084526106d981602086016020860161069d565b601f01601f19169290920160200192915050565b6020815260006105b960208301846106c1565b80356001600160e01b03198116811461071857600080fd5b919050565b60006020828403121561072f57600080fd5b6105b982610700565b80356001600160a01b038116811461071857600080fd5b60006020828403121561076157600080fd5b6105b982610738565b6000806040838503121561077d57600080fd5b61078683610700565b946020939093013593505050565b600080604083850312156107a757600080fd5b823567ffffffffffffffff8111156107be57600080fd5b8301601f810185136107cf57600080fd5b6107de858235602084016105d6565b95602094909401359450505050565b6000806040838503121561080057600080fd5b61078683610738565b60006020828403121561081b57600080fd5b5035919050565b6000825161083481846020870161069d565b9190910192915050565b63ffffffff60e01b8416815260606020820152600061086060608301856106c1565b828103604084015261087281856106c1565b969550505050505056fea26469706673582212209161ccf77ca651079cf7ca63ab02af76c981b46d82708b27350727dd4292764464736f6c63430008150033",
282
- "deployedBytecode": "0x6080604052600436106100a05760003560e01c8063491d261111610064578063491d2611146101d35780635defb40d146101f3578063ae5b102e14610213578063ae682e2e14610233578063d4b839921461024b578063d5bb7f6714610283576100bf565b806309c5eabe146100ff5780630e82fe25146101285780632b5214161461016357806334e48c9e14610185578063442767331461019d576100bf565b366100bf576100bd604051806020016040528060008152506102a3565b005b6100bd6000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506102a392505050565b61011261010d36600461064c565b6102a3565b60405161011f91906106ed565b60405180910390f35b34801561013457600080fd5b5061015561014336600461071d565b60026020526000908152604090205481565b60405190815260200161011f565b34801561016f57600080fd5b5030600090815260208190526040902054610155565b34801561019157600080fd5b50610155600160fd1b81565b3480156101a957600080fd5b506101556101b836600461074f565b6001600160a01b031660009081526020819052604090205490565b3480156101df57600080fd5b506100bd6101ee36600461076a565b610411565b3480156101ff57600080fd5b506100bd61020e366004610794565b610477565b34801561021f57600080fd5b506100bd61022e3660046107ed565b61048c565b34801561023f57600080fd5b50610155600160ff1b81565b34801561025757600080fd5b5060015461026b906001600160a01b031681565b6040516001600160a01b03909116815260200161011f565b34801561028f57600080fd5b506100bd61029e366004610809565b6104f5565b602081015181516060919015610323576001600160e01b03198116600090815260026020526040812054908190036103185760405162461bcd60e51b81526020600482015260136024820152721858d8d95cdcc81c9bdb19481b9bdd081cd95d606a1b60448201526064015b60405180910390fd5b61032181610502565b505b60015460405160009182916001600160a01b03909116903490610347908890610822565b60006040518083038185875af1925050503d8060008114610384576040519150601f19603f3d011682016040523d82523d6000602084013e610389565b606091505b5091509150816103ce5760405162461bcd60e51b815260206004820152601060248201526f195e1958dd5d1a5bdb8819985a5b195960821b604482015260640161030f565b7f57a62eca76fc623c92f161d2a4b851851ece707135ce2af1eec256d660571b6d8386836040516104019392919061083e565b60405180910390a1949350505050565b61041e600160fd1b610502565b6001600160e01b03198216600081815260026020908152604091829020849055815192835282018390527fdb8ed917742b49e83acd1322bcaa8f18b1e5f78a70784c43ea14db7ab50e628d910160405180910390a15050565b610488828051906020012082610411565b5050565b610499600160ff1b610502565b61048882826104f0336104c1876001600160a01b031660009081526020819052604090205490565b6001600160a01b0391909116600090815260208190526040902054600019808818821618908716919091171690565b610513565b6104ff308261048c565b50565b6104ff61050e8261056b565b61057d565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b6000610577338361059b565b92915050565b806104ff57604051634ca8886760e01b815260040160405180910390fd5b6001600160a01b038216600090815260208190526040812054821682145b9392505050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156105f1576105f16105c0565b604051601f8501601f19908116603f01168101908282118183101715610619576106196105c0565b8160405280935085815286868601111561063257600080fd5b858560208301376000602087830101525050509392505050565b60006020828403121561065e57600080fd5b813567ffffffffffffffff81111561067557600080fd5b8201601f8101841361068657600080fd5b610695848235602084016105d6565b949350505050565b60005b838110156106b85781810151838201526020016106a0565b50506000910152565b600081518084526106d981602086016020860161069d565b601f01601f19169290920160200192915050565b6020815260006105b960208301846106c1565b80356001600160e01b03198116811461071857600080fd5b919050565b60006020828403121561072f57600080fd5b6105b982610700565b80356001600160a01b038116811461071857600080fd5b60006020828403121561076157600080fd5b6105b982610738565b6000806040838503121561077d57600080fd5b61078683610700565b946020939093013593505050565b600080604083850312156107a757600080fd5b823567ffffffffffffffff8111156107be57600080fd5b8301601f810185136107cf57600080fd5b6107de858235602084016105d6565b95602094909401359450505050565b6000806040838503121561080057600080fd5b61078683610738565b60006020828403121561081b57600080fd5b5035919050565b6000825161083481846020870161069d565b9190910192915050565b63ffffffff60e01b8416815260606020820152600061086060608301856106c1565b828103604084015261087281856106c1565b969550505050505056fea26469706673582212209161ccf77ca651079cf7ca63ab02af76c981b46d82708b27350727dd4292764464736f6c63430008150033",
283
- "linkReferences": {},
284
- "deployedLinkReferences": {}
285
- }