@lazy-sol/access-control 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ v1.1.0: Contact Size Optimizations
2
+
3
+ - __Breaking Change:__ Solidity 0.8.4 is now required to compile the contracts
4
+ (previously was 0.4.22 for the core contract and 0.6.2 for periphery contracts).
5
+ - Replaced "access denied" error with string literal (0.4.22) with the custom error AccessDenied() (0.8.4)
6
+ - Introduced lighter modification of the "AccessControl" RBAC contract – "AccessControlCore" (RBAC-C).
7
+ The 'core' version of the RBAC contract hides three rarely used external functions from the public ABI,
8
+ making them internal and thus reducing the overall compiled implementation size.
9
+
10
+ | old name | old modifier | new name | new modifier |
11
+ |----------------------|--------------|-----------------------|--------------|
12
+ | `isFeatureEnabled()` | `public` | `isFeatureEnabled()` | `internal` |
13
+ | `isSenderInRole()` | `public` | `_isSenderInRole()` | `internal` |
14
+ | `isOperatorInRole()` | `public` | `_isOperatorInRole()` | `internal` |
15
+
16
+ - Added `_requireSenderInRole()` and `_requireAccessCondition()` internal functions for a convenient and gas-efficient
17
+ way of checking the access permissions and throwing the AccessDenied() error.
18
+ - Added the CHANGELOG (this file)
19
+
20
+ v1.0.7:
21
+ - Exported the RBAC behavior
22
+
23
+ v1.0.6:
24
+ - Updated the documentation
25
+
26
+ v1.0.5:
27
+ - Exported RBAC JavaScript constants and functions
28
+
29
+ v1.0.4:
30
+ - Fixed broken dependencies
31
+ - Upgraded npm libraries
32
+ - Added Sepolia network support
33
+
34
+ v1.0.3:
35
+ - Allowed lower versions of Hardhat to be used
36
+
37
+ v1.0.2:
38
+ - Optimized npm dependencies
package/README.md CHANGED
@@ -4,6 +4,83 @@ 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
+ ## Technical Overview
8
+
9
+ Role-based Access Control (RBAC), or simply Access Control, is the base parent contract to be inherited by other smart
10
+ contracts wishing to enable the RBAC feature. It provides an API to check if a specific operation is permitted globally
11
+ and/or if a particular user has a permission to execute it.
12
+
13
+ It deals with two main entities: features and roles. Features are designed to be used to enable/disable public functions
14
+ of the smart contract (used by a wide audience). User roles are designed to control the access to restricted functions
15
+ of the smart contract (used by a limited set of maintainers).
16
+
17
+ When designing the RBAC-enabled contract, the best practice is to make all public mutative functions controlled with
18
+ their corresponding feature flags, which can be enabled/disabled during smart contact deployment, setup process, and,
19
+ optionally, during contract operation.
20
+
21
+ Restricted access functions must be controlled by their corresponding user roles/permissions and usually can be executed
22
+ by the deployer during smart contract deployment and setup process.
23
+
24
+ After deployment is complete and smart contract setup is verified the deployer should enable the feature flags and
25
+ revoke own permissions to control these flags, as well as permissions to execute restricted access functions.
26
+
27
+ It is possible that smart contract functionalities are enabled in phases, but the intention is that eventually it is
28
+ also possible to set the smart contract to be uncontrolled by anyone and be fully decentralized.
29
+
30
+ It is also possible that the deployer shares its admin permissions with other addresses during the deployment and setup
31
+ process, but eventually all these permissions can be revoked from all the addresses involved.
32
+
33
+ Following diagram summarizes stated below:
34
+
35
+ ![Role-based Access Control (RBAC) Lifecycle](Role-based%20Access%20Control%20%28RBAC%29%20Lifecycle.png)
36
+ Diagram 1. RBAC-enabled smart contract deployment and setup phases. Contract evolves from the fully controlled in the
37
+ initial phases of the setup process to the fully decentralized and uncontrolled in the end.
38
+
39
+ It is important to note that it is not necessary, and not recommended to wait until the last “Setup Complete” phase is
40
+ executed to consider the protocol fully operational in the mainnet. In fact, the best practice is to do the launch after
41
+ the deployer permissions are revoked, but there are admin multisig accounts with the full permissions to control the
42
+ protocol. This kind of approach allows reacting to the security issues, which are more likely to happen in the beginning
43
+ of the protocol operation.
44
+
45
+ ## Special Permissions Mapping
46
+
47
+ Special permissions mapping, `userRoles`, stores special permissions of the smart contract administrators and helpers.
48
+ The mapping is a part of AccessControl and is inherited by the smart contracts using it.
49
+
50
+ The value stored in the mapping is a 256 bits unsigned integer, each bit of that integer represents a particular
51
+ permission. We call a set of permissions a role. Usually, roles are defined as 32 bits unsigned integer constants, but
52
+ extension to 255 bits is possible.
53
+
54
+ Permission with the bit 255 set is a special one. It corresponds to the access manager role `ROLE_ACCESS_MANAGER`
55
+ defined on the Access Control smart contract and allows accounts having that bit set to grant/revoke their permissions
56
+ to other addresses and to enable/disable corresponding features of the smart contract (to update self address “this”
57
+ role – see below).
58
+
59
+ Self address “this” mapping is a special one. It represents the deployed smart contract itself and defines features
60
+ enabled on it. Features control what public functions are enabled and how they behave. Usually, features are defined as
61
+ 32 bits unsigned integer constants, but extension to 255 bits is possible.
62
+
63
+ Access Control is a shared parent for other smart contracts which are free to use any strategy to introduce their
64
+ features and roles. Usually, smart contracts use different values for all the features and roles (see the table in the
65
+ next section).
66
+
67
+ Access manager may revoke its own permissions, including the bit 255. Eventually that allows an access manager to let
68
+ the smart contract “float freely” and be controlled only by the community (via DAO) or by no one at all.
69
+
70
+ ## Comparing with OpenZeppelin
71
+
72
+ Both our and OpenZeppelin Access Control implementations feature a similar API to check/know "who is allowed to do this
73
+ thing".
74
+
75
+ Zeppelin implementation is more flexible:
76
+ * it allows setting unlimited number of roles, while current is limited to 256 different roles
77
+ * it allows setting an admin for each role, while current allows having only one global admin
78
+
79
+ Our implementation is more lightweight:
80
+ * it uses only 1 bit per role, while Zeppelin uses 256 bits
81
+ * it allows setting up to 256 roles at once, in a single transaction, while Zeppelin allows setting only one role in a
82
+ single transaction
83
+
7
84
  ## Installation
8
85
  ```
9
86
  npm i -D @lazy-sol/access-control
@@ -19,6 +19,11 @@
19
19
  "stateMutability": "nonpayable",
20
20
  "type": "constructor"
21
21
  },
22
+ {
23
+ "inputs": [],
24
+ "name": "AccessDenied",
25
+ "type": "error"
26
+ },
22
27
  {
23
28
  "anonymous": false,
24
29
  "inputs": [
@@ -188,68 +193,6 @@
188
193
  "stateMutability": "view",
189
194
  "type": "function"
190
195
  },
191
- {
192
- "inputs": [
193
- {
194
- "internalType": "uint256",
195
- "name": "required",
196
- "type": "uint256"
197
- }
198
- ],
199
- "name": "isFeatureEnabled",
200
- "outputs": [
201
- {
202
- "internalType": "bool",
203
- "name": "",
204
- "type": "bool"
205
- }
206
- ],
207
- "stateMutability": "view",
208
- "type": "function"
209
- },
210
- {
211
- "inputs": [
212
- {
213
- "internalType": "address",
214
- "name": "operator",
215
- "type": "address"
216
- },
217
- {
218
- "internalType": "uint256",
219
- "name": "required",
220
- "type": "uint256"
221
- }
222
- ],
223
- "name": "isOperatorInRole",
224
- "outputs": [
225
- {
226
- "internalType": "bool",
227
- "name": "",
228
- "type": "bool"
229
- }
230
- ],
231
- "stateMutability": "view",
232
- "type": "function"
233
- },
234
- {
235
- "inputs": [
236
- {
237
- "internalType": "uint256",
238
- "name": "required",
239
- "type": "uint256"
240
- }
241
- ],
242
- "name": "isSenderInRole",
243
- "outputs": [
244
- {
245
- "internalType": "bool",
246
- "name": "",
247
- "type": "bool"
248
- }
249
- ],
250
- "stateMutability": "view",
251
- "type": "function"
252
- },
253
196
  {
254
197
  "inputs": [],
255
198
  "name": "target",
@@ -335,8 +278,8 @@
335
278
  "type": "receive"
336
279
  }
337
280
  ],
338
- "bytecode": "0x608060405234801561001057600080fd5b50604051610b40380380610b4083398101604081905261002f9161012f565b80600061003f82600019806100bb565b61004a3082806100bb565b50506001600160a01b0382166100955760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b604482015260640160405180910390fd5b50600180546001600160a01b0319166001600160a01b0392909216919091179055610162565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b80516001600160a01b038116811461012a57600080fd5b919050565b6000806040838503121561014257600080fd5b61014b83610113565b915061015960208401610113565b90509250929050565b6109cf806101716000396000f3fe6080604052600436106100e15760003560e01c8063725f36261161007f578063c688d69311610059578063c688d693146102bc578063d4b83992146102dc578063d5bb7f6714610314578063fcc2c0781461033457610100565b8063725f362614610254578063ae5b102e14610284578063ae682e2e146102a457610100565b806334e48c9e116100bb57806334e48c9e146101c657806344276733146101de578063491d2611146102145780635defb40d1461023457610100565b806309c5eabe146101405780630e82fe25146101695780632b521416146101a457610100565b36610100576100fe60405180602001604052806000815250610354565b005b6100fe6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061035492505050565b61015361014e366004610742565b610354565b60405161016091906107e3565b60405180910390f35b34801561017557600080fd5b50610196610184366004610813565b60026020526000908152604090205481565b604051908152602001610160565b3480156101b057600080fd5b5030600090815260208190526040902054610196565b3480156101d257600080fd5b50610196600160fd1b81565b3480156101ea57600080fd5b506101966101f9366004610845565b6001600160a01b031660009081526020819052604090205490565b34801561022057600080fd5b506100fe61022f366004610860565b6104de565b34801561024057600080fd5b506100fe61024f36600461088a565b610560565b34801561026057600080fd5b5061027461026f3660046108e3565b610575565b6040519015158152602001610160565b34801561029057600080fd5b506100fe61029f3660046108fc565b610597565b3480156102b057600080fd5b50610196600160ff1b81565b3480156102c857600080fd5b506102746102d73660046108fc565b61061c565b3480156102e857600080fd5b506001546102fc906001600160a01b031681565b6040516001600160a01b039091168152602001610160565b34801561032057600080fd5b506100fe61032f3660046108e3565b610645565b34801561034057600080fd5b5061027461034f3660046108e3565b610652565b6020810151815160609190156103f0576001600160e01b03198116600090815260026020526040812054908190036103c95760405162461bcd60e51b81526020600482015260136024820152721858d8d95cdcc81c9bdb19481b9bdd081cd95d606a1b60448201526064015b60405180910390fd5b6103d281610652565b6103ee5760405162461bcd60e51b81526004016103c090610918565b505b60015460405160009182916001600160a01b0390911690349061041490889061093f565b60006040518083038185875af1925050503d8060008114610451576040519150601f19603f3d011682016040523d82523d6000602084013e610456565b606091505b50915091508161049b5760405162461bcd60e51b815260206004820152601060248201526f195e1958dd5d1a5bdb8819985a5b195960821b60448201526064016103c0565b7f57a62eca76fc623c92f161d2a4b851851ece707135ce2af1eec256d660571b6d8386836040516104ce9392919061095b565b60405180910390a1949350505050565b6104eb600160fd1b610652565b6105075760405162461bcd60e51b81526004016103c090610918565b6001600160e01b03198216600081815260026020908152604091829020849055815192835282018390527fdb8ed917742b49e83acd1322bcaa8f18b1e5f78a70784c43ea14db7ab50e628d910160405180910390a15050565b6105718280519060200120826104de565b5050565b30600090815260208190526040812054610591905b8316831490565b92915050565b6105a4600160ff1b610652565b6105c05760405162461bcd60e51b81526004016103c090610918565b6105718282610617336105e8876001600160a01b031660009081526020819052604090205490565b6001600160a01b0391909116600090815260208190526040902054600019808818821618908716919091171690565b61065e565b6001600160a01b03821660009081526020819052604081205461063e9061058a565b9392505050565b61064f3082610597565b50565b6000610591338361061c565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156106e7576106e76106b6565b604051601f8501601f19908116603f0116810190828211818310171561070f5761070f6106b6565b8160405280935085815286868601111561072857600080fd5b858560208301376000602087830101525050509392505050565b60006020828403121561075457600080fd5b813567ffffffffffffffff81111561076b57600080fd5b8201601f8101841361077c57600080fd5b61078b848235602084016106cc565b949350505050565b60005b838110156107ae578181015183820152602001610796565b50506000910152565b600081518084526107cf816020860160208601610793565b601f01601f19169290920160200192915050565b60208152600061063e60208301846107b7565b80356001600160e01b03198116811461080e57600080fd5b919050565b60006020828403121561082557600080fd5b61063e826107f6565b80356001600160a01b038116811461080e57600080fd5b60006020828403121561085757600080fd5b61063e8261082e565b6000806040838503121561087357600080fd5b61087c836107f6565b946020939093013593505050565b6000806040838503121561089d57600080fd5b823567ffffffffffffffff8111156108b457600080fd5b8301601f810185136108c557600080fd5b6108d4858235602084016106cc565b95602094909401359450505050565b6000602082840312156108f557600080fd5b5035919050565b6000806040838503121561090f57600080fd5b61087c8361082e565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b60008251610951818460208701610793565b9190910192915050565b63ffffffff60e01b8416815260606020820152600061097d60608301856107b7565b828103604084015261098f81856107b7565b969550505050505056fea26469706673582212206993ad950cca82a01e8e72c0503e3a006930148e125b67ad2894063853f6078264736f6c63430008150033",
339
- "deployedBytecode": "0x6080604052600436106100e15760003560e01c8063725f36261161007f578063c688d69311610059578063c688d693146102bc578063d4b83992146102dc578063d5bb7f6714610314578063fcc2c0781461033457610100565b8063725f362614610254578063ae5b102e14610284578063ae682e2e146102a457610100565b806334e48c9e116100bb57806334e48c9e146101c657806344276733146101de578063491d2611146102145780635defb40d1461023457610100565b806309c5eabe146101405780630e82fe25146101695780632b521416146101a457610100565b36610100576100fe60405180602001604052806000815250610354565b005b6100fe6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061035492505050565b61015361014e366004610742565b610354565b60405161016091906107e3565b60405180910390f35b34801561017557600080fd5b50610196610184366004610813565b60026020526000908152604090205481565b604051908152602001610160565b3480156101b057600080fd5b5030600090815260208190526040902054610196565b3480156101d257600080fd5b50610196600160fd1b81565b3480156101ea57600080fd5b506101966101f9366004610845565b6001600160a01b031660009081526020819052604090205490565b34801561022057600080fd5b506100fe61022f366004610860565b6104de565b34801561024057600080fd5b506100fe61024f36600461088a565b610560565b34801561026057600080fd5b5061027461026f3660046108e3565b610575565b6040519015158152602001610160565b34801561029057600080fd5b506100fe61029f3660046108fc565b610597565b3480156102b057600080fd5b50610196600160ff1b81565b3480156102c857600080fd5b506102746102d73660046108fc565b61061c565b3480156102e857600080fd5b506001546102fc906001600160a01b031681565b6040516001600160a01b039091168152602001610160565b34801561032057600080fd5b506100fe61032f3660046108e3565b610645565b34801561034057600080fd5b5061027461034f3660046108e3565b610652565b6020810151815160609190156103f0576001600160e01b03198116600090815260026020526040812054908190036103c95760405162461bcd60e51b81526020600482015260136024820152721858d8d95cdcc81c9bdb19481b9bdd081cd95d606a1b60448201526064015b60405180910390fd5b6103d281610652565b6103ee5760405162461bcd60e51b81526004016103c090610918565b505b60015460405160009182916001600160a01b0390911690349061041490889061093f565b60006040518083038185875af1925050503d8060008114610451576040519150601f19603f3d011682016040523d82523d6000602084013e610456565b606091505b50915091508161049b5760405162461bcd60e51b815260206004820152601060248201526f195e1958dd5d1a5bdb8819985a5b195960821b60448201526064016103c0565b7f57a62eca76fc623c92f161d2a4b851851ece707135ce2af1eec256d660571b6d8386836040516104ce9392919061095b565b60405180910390a1949350505050565b6104eb600160fd1b610652565b6105075760405162461bcd60e51b81526004016103c090610918565b6001600160e01b03198216600081815260026020908152604091829020849055815192835282018390527fdb8ed917742b49e83acd1322bcaa8f18b1e5f78a70784c43ea14db7ab50e628d910160405180910390a15050565b6105718280519060200120826104de565b5050565b30600090815260208190526040812054610591905b8316831490565b92915050565b6105a4600160ff1b610652565b6105c05760405162461bcd60e51b81526004016103c090610918565b6105718282610617336105e8876001600160a01b031660009081526020819052604090205490565b6001600160a01b0391909116600090815260208190526040902054600019808818821618908716919091171690565b61065e565b6001600160a01b03821660009081526020819052604081205461063e9061058a565b9392505050565b61064f3082610597565b50565b6000610591338361061c565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156106e7576106e76106b6565b604051601f8501601f19908116603f0116810190828211818310171561070f5761070f6106b6565b8160405280935085815286868601111561072857600080fd5b858560208301376000602087830101525050509392505050565b60006020828403121561075457600080fd5b813567ffffffffffffffff81111561076b57600080fd5b8201601f8101841361077c57600080fd5b61078b848235602084016106cc565b949350505050565b60005b838110156107ae578181015183820152602001610796565b50506000910152565b600081518084526107cf816020860160208601610793565b601f01601f19169290920160200192915050565b60208152600061063e60208301846107b7565b80356001600160e01b03198116811461080e57600080fd5b919050565b60006020828403121561082557600080fd5b61063e826107f6565b80356001600160a01b038116811461080e57600080fd5b60006020828403121561085757600080fd5b61063e8261082e565b6000806040838503121561087357600080fd5b61087c836107f6565b946020939093013593505050565b6000806040838503121561089d57600080fd5b823567ffffffffffffffff8111156108b457600080fd5b8301601f810185136108c557600080fd5b6108d4858235602084016106cc565b95602094909401359450505050565b6000602082840312156108f557600080fd5b5035919050565b6000806040838503121561090f57600080fd5b61087c8361082e565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b60008251610951818460208701610793565b9190910192915050565b63ffffffff60e01b8416815260606020820152600061097d60608301856107b7565b828103604084015261098f81856107b7565b969550505050505056fea26469706673582212206993ad950cca82a01e8e72c0503e3a006930148e125b67ad2894063853f6078264736f6c63430008150033",
281
+ "bytecode": "0x608060405234801561001057600080fd5b50604051610a23380380610a2383398101604081905261002f9161012f565b80600061003f82600019806100bb565b61004a3082806100bb565b50506001600160a01b0382166100955760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b604482015260640160405180910390fd5b50600180546001600160a01b0319166001600160a01b0392909216919091179055610162565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b80516001600160a01b038116811461012a57600080fd5b919050565b6000806040838503121561014257600080fd5b61014b83610113565b915061015960208401610113565b90509250929050565b6108b2806101716000396000f3fe6080604052600436106100a05760003560e01c8063491d261111610064578063491d2611146101d35780635defb40d146101f3578063ae5b102e14610213578063ae682e2e14610233578063d4b839921461024b578063d5bb7f6714610283576100bf565b806309c5eabe146100ff5780630e82fe25146101285780632b5214161461016357806334e48c9e14610185578063442767331461019d576100bf565b366100bf576100bd604051806020016040528060008152506102a3565b005b6100bd6000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506102a392505050565b61011261010d36600461064c565b6102a3565b60405161011f91906106ed565b60405180910390f35b34801561013457600080fd5b5061015561014336600461071d565b60026020526000908152604090205481565b60405190815260200161011f565b34801561016f57600080fd5b5030600090815260208190526040902054610155565b34801561019157600080fd5b50610155600160fd1b81565b3480156101a957600080fd5b506101556101b836600461074f565b6001600160a01b031660009081526020819052604090205490565b3480156101df57600080fd5b506100bd6101ee36600461076a565b610411565b3480156101ff57600080fd5b506100bd61020e366004610794565b610477565b34801561021f57600080fd5b506100bd61022e3660046107ed565b61048c565b34801561023f57600080fd5b50610155600160ff1b81565b34801561025757600080fd5b5060015461026b906001600160a01b031681565b6040516001600160a01b03909116815260200161011f565b34801561028f57600080fd5b506100bd61029e366004610809565b6104f5565b602081015181516060919015610323576001600160e01b03198116600090815260026020526040812054908190036103185760405162461bcd60e51b81526020600482015260136024820152721858d8d95cdcc81c9bdb19481b9bdd081cd95d606a1b60448201526064015b60405180910390fd5b61032181610502565b505b60015460405160009182916001600160a01b03909116903490610347908890610822565b60006040518083038185875af1925050503d8060008114610384576040519150601f19603f3d011682016040523d82523d6000602084013e610389565b606091505b5091509150816103ce5760405162461bcd60e51b815260206004820152601060248201526f195e1958dd5d1a5bdb8819985a5b195960821b604482015260640161030f565b7f57a62eca76fc623c92f161d2a4b851851ece707135ce2af1eec256d660571b6d8386836040516104019392919061083e565b60405180910390a1949350505050565b61041e600160fd1b610502565b6001600160e01b03198216600081815260026020908152604091829020849055815192835282018390527fdb8ed917742b49e83acd1322bcaa8f18b1e5f78a70784c43ea14db7ab50e628d910160405180910390a15050565b610488828051906020012082610411565b5050565b610499600160ff1b610502565b61048882826104f0336104c1876001600160a01b031660009081526020819052604090205490565b6001600160a01b0391909116600090815260208190526040902054600019808818821618908716919091171690565b610513565b6104ff308261048c565b50565b6104ff61050e8261056b565b61057d565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b6000610577338361059b565b92915050565b806104ff57604051634ca8886760e01b815260040160405180910390fd5b6001600160a01b038216600090815260208190526040812054821682145b9392505050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156105f1576105f16105c0565b604051601f8501601f19908116603f01168101908282118183101715610619576106196105c0565b8160405280935085815286868601111561063257600080fd5b858560208301376000602087830101525050509392505050565b60006020828403121561065e57600080fd5b813567ffffffffffffffff81111561067557600080fd5b8201601f8101841361068657600080fd5b610695848235602084016105d6565b949350505050565b60005b838110156106b85781810151838201526020016106a0565b50506000910152565b600081518084526106d981602086016020860161069d565b601f01601f19169290920160200192915050565b6020815260006105b960208301846106c1565b80356001600160e01b03198116811461071857600080fd5b919050565b60006020828403121561072f57600080fd5b6105b982610700565b80356001600160a01b038116811461071857600080fd5b60006020828403121561076157600080fd5b6105b982610738565b6000806040838503121561077d57600080fd5b61078683610700565b946020939093013593505050565b600080604083850312156107a757600080fd5b823567ffffffffffffffff8111156107be57600080fd5b8301601f810185136107cf57600080fd5b6107de858235602084016105d6565b95602094909401359450505050565b6000806040838503121561080057600080fd5b61078683610738565b60006020828403121561081b57600080fd5b5035919050565b6000825161083481846020870161069d565b9190910192915050565b63ffffffff60e01b8416815260606020820152600061086060608301856106c1565b828103604084015261087281856106c1565b969550505050505056fea2646970667358221220e3c74e6e9d12e9f897ad014f9a9508e19f07aa6158ea118e7b0f262e5ef3629764736f6c63430008150033",
282
+ "deployedBytecode": "0x6080604052600436106100a05760003560e01c8063491d261111610064578063491d2611146101d35780635defb40d146101f3578063ae5b102e14610213578063ae682e2e14610233578063d4b839921461024b578063d5bb7f6714610283576100bf565b806309c5eabe146100ff5780630e82fe25146101285780632b5214161461016357806334e48c9e14610185578063442767331461019d576100bf565b366100bf576100bd604051806020016040528060008152506102a3565b005b6100bd6000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506102a392505050565b61011261010d36600461064c565b6102a3565b60405161011f91906106ed565b60405180910390f35b34801561013457600080fd5b5061015561014336600461071d565b60026020526000908152604090205481565b60405190815260200161011f565b34801561016f57600080fd5b5030600090815260208190526040902054610155565b34801561019157600080fd5b50610155600160fd1b81565b3480156101a957600080fd5b506101556101b836600461074f565b6001600160a01b031660009081526020819052604090205490565b3480156101df57600080fd5b506100bd6101ee36600461076a565b610411565b3480156101ff57600080fd5b506100bd61020e366004610794565b610477565b34801561021f57600080fd5b506100bd61022e3660046107ed565b61048c565b34801561023f57600080fd5b50610155600160ff1b81565b34801561025757600080fd5b5060015461026b906001600160a01b031681565b6040516001600160a01b03909116815260200161011f565b34801561028f57600080fd5b506100bd61029e366004610809565b6104f5565b602081015181516060919015610323576001600160e01b03198116600090815260026020526040812054908190036103185760405162461bcd60e51b81526020600482015260136024820152721858d8d95cdcc81c9bdb19481b9bdd081cd95d606a1b60448201526064015b60405180910390fd5b61032181610502565b505b60015460405160009182916001600160a01b03909116903490610347908890610822565b60006040518083038185875af1925050503d8060008114610384576040519150601f19603f3d011682016040523d82523d6000602084013e610389565b606091505b5091509150816103ce5760405162461bcd60e51b815260206004820152601060248201526f195e1958dd5d1a5bdb8819985a5b195960821b604482015260640161030f565b7f57a62eca76fc623c92f161d2a4b851851ece707135ce2af1eec256d660571b6d8386836040516104019392919061083e565b60405180910390a1949350505050565b61041e600160fd1b610502565b6001600160e01b03198216600081815260026020908152604091829020849055815192835282018390527fdb8ed917742b49e83acd1322bcaa8f18b1e5f78a70784c43ea14db7ab50e628d910160405180910390a15050565b610488828051906020012082610411565b5050565b610499600160ff1b610502565b61048882826104f0336104c1876001600160a01b031660009081526020819052604090205490565b6001600160a01b0391909116600090815260208190526040902054600019808818821618908716919091171690565b610513565b6104ff308261048c565b50565b6104ff61050e8261056b565b61057d565b6001600160a01b0383166000818152602081815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b6000610577338361059b565b92915050565b806104ff57604051634ca8886760e01b815260040160405180910390fd5b6001600160a01b038216600090815260208190526040812054821682145b9392505050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156105f1576105f16105c0565b604051601f8501601f19908116603f01168101908282118183101715610619576106196105c0565b8160405280935085815286868601111561063257600080fd5b858560208301376000602087830101525050509392505050565b60006020828403121561065e57600080fd5b813567ffffffffffffffff81111561067557600080fd5b8201601f8101841361068657600080fd5b610695848235602084016105d6565b949350505050565b60005b838110156106b85781810151838201526020016106a0565b50506000910152565b600081518084526106d981602086016020860161069d565b601f01601f19169290920160200192915050565b6020815260006105b960208301846106c1565b80356001600160e01b03198116811461071857600080fd5b919050565b60006020828403121561072f57600080fd5b6105b982610700565b80356001600160a01b038116811461071857600080fd5b60006020828403121561076157600080fd5b6105b982610738565b6000806040838503121561077d57600080fd5b61078683610700565b946020939093013593505050565b600080604083850312156107a757600080fd5b823567ffffffffffffffff8111156107be57600080fd5b8301601f810185136107cf57600080fd5b6107de858235602084016105d6565b95602094909401359450505050565b6000806040838503121561080057600080fd5b61078683610738565b60006020828403121561081b57600080fd5b5035919050565b6000825161083481846020870161069d565b9190910192915050565b63ffffffff60e01b8416815260606020820152600061086060608301856106c1565b828103604084015261087281856106c1565b969550505050505056fea2646970667358221220e3c74e6e9d12e9f897ad014f9a9508e19f07aa6158ea118e7b0f262e5ef3629764736f6c63430008150033",
340
283
  "linkReferences": {},
341
284
  "deployedLinkReferences": {}
342
285
  }
@@ -1,5 +1,11 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity >=0.4.22; // require with message (0.4.22), pure/view modifiers (0.4.16), hardhat (0.4.11)
2
+ // custom errors (0.8.4)
3
+ // require with message (0.4.22)
4
+ // pure/view modifiers (0.4.16)
5
+ // hardhat (0.4.11)
6
+ pragma solidity >=0.8.4;
7
+
8
+ import {AccessControlCore} from "./AccessControlCore.sol";
3
9
 
4
10
  /**
5
11
  * @title Role-based Access Control (RBAC)
@@ -50,189 +56,14 @@ pragma solidity >=0.4.22; // require with message (0.4.22), pure/view modifiers
50
56
  *
51
57
  * @author Basil Gorin
52
58
  */
53
- abstract contract AccessControl {
54
- /**
55
- * @dev Privileged addresses with defined roles/permissions
56
- * @dev In the context of ERC20/ERC721 tokens these can be permissions to
57
- * allow minting or burning tokens, transferring on behalf and so on
58
- *
59
- * @dev Maps user address to the permissions bitmask (role), where each bit
60
- * represents a permission
61
- * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
62
- * represents all possible permissions
63
- * @dev 'This' address mapping represents global features of the smart contract
64
- *
65
- * @dev We keep the mapping private to prevent direct writes to it from the inheriting
66
- * contracts, `getRole()` and `updateRole()` functions should be used instead
67
- */
68
- mapping(address => uint256) private userRoles;
69
-
70
- /**
71
- * @notice Access manager is responsible for assigning the roles to users,
72
- * enabling/disabling global features of the smart contract
73
- * @notice Access manager can add, remove and update user roles,
74
- * remove and update global features
75
- *
76
- * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
77
- * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
78
- */
79
- uint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;
80
-
81
- /**
82
- * @dev Bitmask representing all the possible permissions (super admin role)
83
- * @dev Has all the bits are enabled (2^256 - 1 value)
84
- */
85
- uint256 internal constant FULL_PRIVILEGES_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
86
-
87
- /**
88
- * @dev Fired in updateRole() and updateFeatures()
89
- *
90
- * @param operator address which was granted/revoked permissions
91
- * @param requested permissions requested
92
- * @param assigned permissions effectively set
93
- */
94
- event RoleUpdated(address indexed operator, uint256 requested, uint256 assigned);
95
-
96
- /**
97
- * @notice Function modifier making a function defined as public behave as restricted
98
- * (so that only a pre-configured set of accounts can execute it)
99
- *
100
- * @param role the role transaction executor is required to have;
101
- * the function throws an "access denied" exception if this condition is not met
102
- */
103
- modifier restrictedTo(uint256 role) {
104
- // verify the access permission
105
- require(isSenderInRole(role), "access denied");
106
-
107
- // execute the rest of the function
108
- _;
109
- }
110
-
59
+ contract AccessControl is AccessControlCore {
111
60
  /**
112
61
  * @notice Creates an access control instance, setting the contract owner to have full privileges
113
62
  *
114
63
  * @param _owner smart contract owner having full privileges, can be zero
115
64
  * @param _features initial features mask of the contract, can be zero
116
65
  */
117
- constructor(address _owner, uint256 _features) internal { // visibility modifier is required to be compilable with 0.6.x
118
- // grant owner full privileges
119
- __setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
120
- // update initial features bitmask
121
- __setRole(address(this), _features, _features);
122
- }
123
-
124
- /**
125
- * @notice Retrieves globally set of features enabled
126
- *
127
- * @dev Effectively reads userRoles role for the contract itself
128
- *
129
- * @return 256-bit bitmask of the features enabled
130
- */
131
- function features() public view returns (uint256) {
132
- // features are stored in 'this' address mapping of `userRoles`
133
- return getRole(address(this));
134
- }
135
-
136
- /**
137
- * @notice Updates set of the globally enabled features (`features`),
138
- * taking into account sender's permissions
139
- *
140
- * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
141
- * @dev Function is left for backward compatibility with older versions
142
- *
143
- * @param _mask bitmask representing a set of features to enable/disable
144
- */
145
- function updateFeatures(uint256 _mask) public {
146
- // delegate call to `updateRole`
147
- updateRole(address(this), _mask);
148
- }
149
-
150
- /**
151
- * @notice Reads the permissions (role) for a given user from the `userRoles` mapping
152
- * (privileged addresses with defined roles/permissions)
153
- * @notice In the context of ERC20/ERC721 tokens these can be permissions to
154
- * allow minting or burning tokens, transferring on behalf and so on
155
- *
156
- * @dev Having a simple getter instead of making the mapping public
157
- * allows enforcing the encapsulation of the mapping and protects from
158
- * writing to it directly in the inheriting smart contracts
159
- *
160
- * @param operator address of a user to read permissions for,
161
- * or self address to read global features of the smart contract
162
- */
163
- function getRole(address operator) public view returns(uint256) {
164
- // read the value from `userRoles` and return
165
- return userRoles[operator];
166
- }
167
-
168
- /**
169
- * @notice Updates set of permissions (role) for a given user,
170
- * taking into account sender's permissions.
171
- *
172
- * @dev Setting role to zero is equivalent to removing an all permissions
173
- * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
174
- * copying senders' permissions (role) to the user
175
- * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
176
- *
177
- * @param operator address of a user to alter permissions for,
178
- * or self address to alter global features of the smart contract
179
- * @param role bitmask representing a set of permissions to
180
- * enable/disable for a user specified
181
- */
182
- function updateRole(address operator, uint256 role) public {
183
- // caller must have a permission to update user roles
184
- require(isSenderInRole(ROLE_ACCESS_MANAGER), "access denied");
185
-
186
- // evaluate the role and reassign it
187
- __setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
188
- }
189
-
190
- /**
191
- * @notice Determines the permission bitmask an operator can set on the
192
- * target permission set
193
- * @notice Used to calculate the permission bitmask to be set when requested
194
- * in `updateRole` and `updateFeatures` functions
195
- *
196
- * @dev Calculated based on:
197
- * 1) operator's own permission set read from userRoles[operator]
198
- * 2) target permission set - what is already set on the target
199
- * 3) desired permission set - what do we want set target to
200
- *
201
- * @dev Corner cases:
202
- * 1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
203
- * `desired` bitset is returned regardless of the `target` permission set value
204
- * (what operator sets is what they get)
205
- * 2) Operator with no permissions (zero bitset):
206
- * `target` bitset is returned regardless of the `desired` value
207
- * (operator has no authority and cannot modify anything)
208
- *
209
- * @dev Example:
210
- * Consider an operator with the permissions bitmask 00001111
211
- * is about to modify the target permission set 01010101
212
- * Operator wants to set that permission set to 00110011
213
- * Based on their role, an operator has the permissions
214
- * to update only lowest 4 bits on the target, meaning that
215
- * high 4 bits of the target set in this example is left
216
- * unchanged and low 4 bits get changed as desired: 01010011
217
- *
218
- * @param operator address of the contract operator which is about to set the permissions
219
- * @param target input set of permissions to operator is going to modify
220
- * @param desired desired set of permissions operator would like to set
221
- * @return resulting set of permissions given operator will set
222
- */
223
- function _evaluateBy(address operator, uint256 target, uint256 desired) internal view returns (uint256) {
224
- // read operator's permissions
225
- uint256 p = getRole(operator);
226
-
227
- // taking into account operator's permissions,
228
- // 1) enable the permissions desired on the `target`
229
- target |= p & desired;
230
- // 2) disable the permissions desired on the `target`
231
- target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));
232
-
233
- // return calculated result
234
- return target;
235
- }
66
+ constructor(address _owner, uint256 _features) AccessControlCore(_owner, _features) {} // visibility modifier is required to be compilable with 0.6.x
236
67
 
237
68
  /**
238
69
  * @notice Checks if requested set of features is enabled globally on the contract
@@ -241,8 +72,8 @@ abstract contract AccessControl {
241
72
  * @return true if all the features requested are enabled, false otherwise
242
73
  */
243
74
  function isFeatureEnabled(uint256 required) public view returns (bool) {
244
- // delegate call to `__hasRole`, passing `features` property
245
- return __hasRole(features(), required);
75
+ // delegate to internal `_isFeatureEnabled`
76
+ return _isFeatureEnabled(required);
246
77
  }
247
78
 
248
79
  /**
@@ -252,8 +83,8 @@ abstract contract AccessControl {
252
83
  * @return true if all the permissions requested are enabled, false otherwise
253
84
  */
254
85
  function isSenderInRole(uint256 required) public view returns (bool) {
255
- // delegate call to `isOperatorInRole`, passing transaction sender
256
- return isOperatorInRole(msg.sender, required);
86
+ // delegate to internal `_isSenderInRole`
87
+ return _isSenderInRole(required);
257
88
  }
258
89
 
259
90
  /**
@@ -264,42 +95,7 @@ abstract contract AccessControl {
264
95
  * @return true if all the permissions requested are enabled, false otherwise
265
96
  */
266
97
  function isOperatorInRole(address operator, uint256 required) public view returns (bool) {
267
- // delegate call to `__hasRole`, passing operator's permissions (role)
268
- return __hasRole(getRole(operator), required);
269
- }
270
-
271
- /**
272
- * @dev Sets the `assignedRole` role to the operator, logs both `requestedRole` and `actualRole`
273
- *
274
- * @dev Unsafe:
275
- * provides direct write access to `userRoles` mapping without any security checks,
276
- * doesn't verify the executor (msg.sender) permissions,
277
- * must be kept private at all times
278
- *
279
- * @param operator address of a user to alter permissions for,
280
- * or self address to alter global features of the smart contract
281
- * @param requestedRole bitmask representing a set of permissions requested
282
- * to be enabled/disabled for a user specified, used only to be logged into event
283
- * @param assignedRole bitmask representing a set of permissions to
284
- * enable/disable for a user specified, used to update the mapping and to be logged into event
285
- */
286
- function __setRole(address operator, uint256 requestedRole, uint256 assignedRole) private {
287
- // assign the role to the operator
288
- userRoles[operator] = assignedRole;
289
-
290
- // fire an event
291
- emit RoleUpdated(operator, requestedRole, assignedRole);
292
- }
293
-
294
- /**
295
- * @dev Checks if role `actual` contains all the permissions required `required`
296
- *
297
- * @param actual existent role
298
- * @param required required role
299
- * @return true if actual has required role (all permissions), false otherwise
300
- */
301
- function __hasRole(uint256 actual, uint256 required) private pure returns (bool) {
302
- // check the bitmask for the role required and return the result
303
- return actual & required == required;
98
+ // delegate to internal `_isOperatorInRole`
99
+ return _isOperatorInRole(operator, required);
304
100
  }
305
101
  }
@@ -0,0 +1,353 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // custom errors (0.8.4)
3
+ // require with message (0.4.22)
4
+ // pure/view modifiers (0.4.16)
5
+ // hardhat (0.4.11)
6
+ pragma solidity >=0.8.4;
7
+
8
+ /**
9
+ * @title Role-based Access Control Core (RBAC-C)
10
+ *
11
+ * @notice Access control smart contract provides an API to check
12
+ * if a specific operation is permitted globally and/or
13
+ * if a particular user has a permission to execute it.
14
+ *
15
+ * @notice This contract is inherited by other contracts requiring the role-based access control (RBAC)
16
+ * protection for the restricted access functions
17
+ *
18
+ * @notice It deals with two main entities: features and roles.
19
+ *
20
+ * @notice Features are designed to be used to enable/disable public functions
21
+ * of the smart contract (used by a wide audience).
22
+ * @notice User roles are designed to control the access to restricted functions
23
+ * of the smart contract (used by a limited set of maintainers).
24
+ *
25
+ * @notice Terms "role", "permissions" and "set of permissions" have equal meaning
26
+ * in the documentation text and may be used interchangeably.
27
+ * @notice Terms "permission", "single permission" implies only one permission bit set.
28
+ *
29
+ * @notice Access manager is a special role which allows to grant/revoke other roles.
30
+ * Access managers can only grant/revoke permissions which they have themselves.
31
+ * As an example, access manager with no other roles set can only grant/revoke its own
32
+ * access manager permission and nothing else.
33
+ *
34
+ * @notice Access manager permission should be treated carefully, as a super admin permission:
35
+ * Access manager with even no other permission can interfere with another account by
36
+ * granting own access manager permission to it and effectively creating more powerful
37
+ * permission set than its own.
38
+ *
39
+ * @dev Both current and OpenZeppelin AccessControl implementations feature a similar API
40
+ * to check/know "who is allowed to do this thing".
41
+ * @dev Zeppelin implementation is more flexible:
42
+ * - it allows setting unlimited number of roles, while current is limited to 256 different roles
43
+ * - it allows setting an admin for each role, while current allows having only one global admin
44
+ * @dev Current implementation is more lightweight:
45
+ * - it uses only 1 bit per role, while Zeppelin uses 256 bits
46
+ * - it allows setting up to 256 roles at once, in a single transaction, while Zeppelin allows
47
+ * setting only one role in a single transaction
48
+ *
49
+ * @dev This smart contract is designed to be inherited by other
50
+ * smart contracts which require access control management capabilities.
51
+ *
52
+ * @dev Access manager permission has a bit 255 set.
53
+ * This bit must not be used by inheriting contracts for any other permissions/features.
54
+ *
55
+ * @dev The 'core' version of the RBAC contract hides three rarely used external functions from the public ABI,
56
+ * making them internal and thus reducing the overall compiled implementation size.
57
+ * isFeatureEnabled() public -> _isFeatureEnabled() internal
58
+ * isSenderInRole() public -> _isSenderInRole() internal
59
+ * isOperatorInRole() public -> _isOperatorInRole() internal
60
+ *
61
+ * @custom:since 1.1.0
62
+ *
63
+ * @author Basil Gorin
64
+ */
65
+ abstract contract AccessControlCore {
66
+ /**
67
+ * @dev Privileged addresses with defined roles/permissions
68
+ * @dev In the context of ERC20/ERC721 tokens these can be permissions to
69
+ * allow minting or burning tokens, transferring on behalf and so on
70
+ *
71
+ * @dev Maps user address to the permissions bitmask (role), where each bit
72
+ * represents a permission
73
+ * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
74
+ * represents all possible permissions
75
+ * @dev 'This' address mapping represents global features of the smart contract
76
+ *
77
+ * @dev We keep the mapping private to prevent direct writes to it from the inheriting
78
+ * contracts, `getRole()` and `updateRole()` functions should be used instead
79
+ */
80
+ mapping(address => uint256) private userRoles;
81
+
82
+ /**
83
+ * @notice Access manager is responsible for assigning the roles to users,
84
+ * enabling/disabling global features of the smart contract
85
+ * @notice Access manager can add, remove and update user roles,
86
+ * remove and update global features
87
+ *
88
+ * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
89
+ * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
90
+ */
91
+ uint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;
92
+
93
+ /**
94
+ * @dev Bitmask representing all the possible permissions (super admin role)
95
+ * @dev Has all the bits are enabled (2^256 - 1 value)
96
+ */
97
+ uint256 internal constant FULL_PRIVILEGES_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
98
+
99
+ /**
100
+ * @notice Thrown when a function is executed by an account that does not have
101
+ * the required access permission(s) (role)
102
+ *
103
+ * @dev This error is used to enforce role-based access control (RBAC) restrictions
104
+ */
105
+ error AccessDenied();
106
+
107
+ /**
108
+ * @dev Fired in updateRole() and updateFeatures()
109
+ *
110
+ * @param operator address which was granted/revoked permissions
111
+ * @param requested permissions requested
112
+ * @param assigned permissions effectively set
113
+ */
114
+ event RoleUpdated(address indexed operator, uint256 requested, uint256 assigned);
115
+
116
+ /**
117
+ * @notice Function modifier making a function defined as public behave as restricted
118
+ * (so that only a pre-configured set of accounts can execute it)
119
+ *
120
+ * @param role the role transaction executor is required to have;
121
+ * the function throws an "access denied" exception if this condition is not met
122
+ */
123
+ modifier restrictedTo(uint256 role) {
124
+ // verify the access permission
125
+ _requireSenderInRole(role);
126
+
127
+ // execute the rest of the function
128
+ _;
129
+ }
130
+
131
+ /**
132
+ * @notice Creates an access control instance, setting the contract owner to have full privileges
133
+ *
134
+ * @param _owner smart contract owner having full privileges, can be zero
135
+ * @param _features initial features mask of the contract, can be zero
136
+ */
137
+ constructor(address _owner, uint256 _features) { // visibility modifier is required to be compilable with 0.6.x
138
+ // grant owner full privileges
139
+ __setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
140
+ // update initial features bitmask
141
+ __setRole(address(this), _features, _features);
142
+ }
143
+
144
+ /**
145
+ * @notice Retrieves globally set of features enabled
146
+ *
147
+ * @dev Effectively reads userRoles role for the contract itself
148
+ *
149
+ * @return 256-bit bitmask of the features enabled
150
+ */
151
+ function features() public view returns (uint256) {
152
+ // features are stored in 'this' address mapping of `userRoles`
153
+ return getRole(address(this));
154
+ }
155
+
156
+ /**
157
+ * @notice Updates set of the globally enabled features (`features`),
158
+ * taking into account sender's permissions
159
+ *
160
+ * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
161
+ * @dev Function is left for backward compatibility with older versions
162
+ *
163
+ * @param _mask bitmask representing a set of features to enable/disable
164
+ */
165
+ function updateFeatures(uint256 _mask) public {
166
+ // delegate call to `updateRole`
167
+ updateRole(address(this), _mask);
168
+ }
169
+
170
+ /**
171
+ * @notice Reads the permissions (role) for a given user from the `userRoles` mapping
172
+ * (privileged addresses with defined roles/permissions)
173
+ * @notice In the context of ERC20/ERC721 tokens these can be permissions to
174
+ * allow minting or burning tokens, transferring on behalf and so on
175
+ *
176
+ * @dev Having a simple getter instead of making the mapping public
177
+ * allows enforcing the encapsulation of the mapping and protects from
178
+ * writing to it directly in the inheriting smart contracts
179
+ *
180
+ * @param operator address of a user to read permissions for,
181
+ * or self address to read global features of the smart contract
182
+ */
183
+ function getRole(address operator) public view returns(uint256) {
184
+ // read the value from `userRoles` and return
185
+ return userRoles[operator];
186
+ }
187
+
188
+ /**
189
+ * @notice Updates set of permissions (role) for a given user,
190
+ * taking into account sender's permissions.
191
+ *
192
+ * @dev Setting role to zero is equivalent to removing an all permissions
193
+ * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
194
+ * copying senders' permissions (role) to the user
195
+ * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
196
+ *
197
+ * @param operator address of a user to alter permissions for,
198
+ * or self address to alter global features of the smart contract
199
+ * @param role bitmask representing a set of permissions to
200
+ * enable/disable for a user specified
201
+ */
202
+ function updateRole(address operator, uint256 role) public {
203
+ // caller must have a permission to update user roles
204
+ _requireSenderInRole(ROLE_ACCESS_MANAGER);
205
+
206
+ // evaluate the role and reassign it
207
+ __setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
208
+ }
209
+
210
+ /**
211
+ * @notice Determines the permission bitmask an operator can set on the
212
+ * target permission set
213
+ * @notice Used to calculate the permission bitmask to be set when requested
214
+ * in `updateRole` and `updateFeatures` functions
215
+ *
216
+ * @dev Calculated based on:
217
+ * 1) operator's own permission set read from userRoles[operator]
218
+ * 2) target permission set - what is already set on the target
219
+ * 3) desired permission set - what do we want set target to
220
+ *
221
+ * @dev Corner cases:
222
+ * 1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
223
+ * `desired` bitset is returned regardless of the `target` permission set value
224
+ * (what operator sets is what they get)
225
+ * 2) Operator with no permissions (zero bitset):
226
+ * `target` bitset is returned regardless of the `desired` value
227
+ * (operator has no authority and cannot modify anything)
228
+ *
229
+ * @dev Example:
230
+ * Consider an operator with the permissions bitmask 00001111
231
+ * is about to modify the target permission set 01010101
232
+ * Operator wants to set that permission set to 00110011
233
+ * Based on their role, an operator has the permissions
234
+ * to update only lowest 4 bits on the target, meaning that
235
+ * high 4 bits of the target set in this example is left
236
+ * unchanged and low 4 bits get changed as desired: 01010011
237
+ *
238
+ * @param operator address of the contract operator which is about to set the permissions
239
+ * @param target input set of permissions to operator is going to modify
240
+ * @param desired desired set of permissions operator would like to set
241
+ * @return resulting set of permissions given operator will set
242
+ */
243
+ function _evaluateBy(address operator, uint256 target, uint256 desired) internal view returns (uint256) {
244
+ // read operator's permissions
245
+ uint256 p = getRole(operator);
246
+
247
+ // taking into account operator's permissions,
248
+ // 1) enable the permissions desired on the `target`
249
+ target |= p & desired;
250
+ // 2) disable the permissions desired on the `target`
251
+ target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));
252
+
253
+ // return calculated result
254
+ return target;
255
+ }
256
+
257
+ /**
258
+ * @notice Ensures that the transaction sender has the required access permission(s) (role)
259
+ *
260
+ * @dev Reverts with an `AccessDenied` error if the sender does not have the required role
261
+ *
262
+ * @param required the set of permissions (role) that the transaction sender is required to have
263
+ */
264
+ function _requireSenderInRole(uint256 required) internal view {
265
+ // check if the transaction has the required permission(s),
266
+ // reverting with the "access denied" error if not
267
+ _requireAccessCondition(_isSenderInRole(required));
268
+ }
269
+
270
+ /**
271
+ * @notice Ensures that a specific condition is met
272
+ *
273
+ * @dev Reverts with an `AccessDenied` error if the condition is not met
274
+ *
275
+ * @param condition the condition that needs to be true for the function to proceed
276
+ */
277
+ function _requireAccessCondition(bool condition) internal pure {
278
+ // check if the condition holds
279
+ if(!condition) {
280
+ // revert with the "access denied" error if not
281
+ revert AccessDenied();
282
+ }
283
+ }
284
+
285
+ /**
286
+ * @notice Checks if requested set of features is enabled globally on the contract
287
+ *
288
+ * @param required set of features to check against
289
+ * @return true if all the features requested are enabled, false otherwise
290
+ */
291
+ function _isFeatureEnabled(uint256 required) internal view returns (bool) {
292
+ // delegate call to `__hasRole`, passing `features` property
293
+ return __hasRole(features(), required);
294
+ }
295
+
296
+ /**
297
+ * @notice Checks if transaction sender `msg.sender` has all the permissions required
298
+ *
299
+ * @param required set of permissions (role) to check against
300
+ * @return true if all the permissions requested are enabled, false otherwise
301
+ */
302
+ function _isSenderInRole(uint256 required) internal view returns (bool) {
303
+ // delegate call to `isOperatorInRole`, passing transaction sender
304
+ return _isOperatorInRole(msg.sender, required);
305
+ }
306
+
307
+ /**
308
+ * @notice Checks if operator has all the permissions (role) required
309
+ *
310
+ * @param operator address of the user to check role for
311
+ * @param required set of permissions (role) to check
312
+ * @return true if all the permissions requested are enabled, false otherwise
313
+ */
314
+ function _isOperatorInRole(address operator, uint256 required) internal view returns (bool) {
315
+ // delegate call to `__hasRole`, passing operator's permissions (role)
316
+ return __hasRole(getRole(operator), required);
317
+ }
318
+
319
+ /**
320
+ * @dev Sets the `assignedRole` role to the operator, logs both `requestedRole` and `actualRole`
321
+ *
322
+ * @dev Unsafe:
323
+ * provides direct write access to `userRoles` mapping without any security checks,
324
+ * doesn't verify the executor (msg.sender) permissions,
325
+ * must be kept private at all times
326
+ *
327
+ * @param operator address of a user to alter permissions for,
328
+ * or self address to alter global features of the smart contract
329
+ * @param requestedRole bitmask representing a set of permissions requested
330
+ * to be enabled/disabled for a user specified, used only to be logged into event
331
+ * @param assignedRole bitmask representing a set of permissions to
332
+ * enable/disable for a user specified, used to update the mapping and to be logged into event
333
+ */
334
+ function __setRole(address operator, uint256 requestedRole, uint256 assignedRole) private {
335
+ // assign the role to the operator
336
+ userRoles[operator] = assignedRole;
337
+
338
+ // fire an event
339
+ emit RoleUpdated(operator, requestedRole, assignedRole);
340
+ }
341
+
342
+ /**
343
+ * @dev Checks if role `actual` contains all the permissions required `required`
344
+ *
345
+ * @param actual existent role
346
+ * @param required required role
347
+ * @return true if actual has required role (all permissions), false otherwise
348
+ */
349
+ function __hasRole(uint256 actual, uint256 required) private pure returns (bool) {
350
+ // check the bitmask for the role required and return the result
351
+ return actual & required == required;
352
+ }
353
+ }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity >=0.6.2;
2
+ pragma solidity >=0.8.4;
3
3
 
4
4
  import "./OwnableToAccessControlAdapter.sol";
5
5
 
@@ -1,5 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity >=0.6.2; // breaking changes in .call() (0.5.0), allow .call{}() (0.6.2)
2
+ // breaking changes in .call() (0.5.0)
3
+ // allow .call{}() (0.6.2)
4
+ pragma solidity >=0.8.4;
3
5
 
4
6
  import "./AccessControl.sol";
5
7
 
@@ -63,7 +65,7 @@ import "./AccessControl.sol";
63
65
  *
64
66
  * @author Basil Gorin
65
67
  */
66
- contract OwnableToAccessControlAdapter is AccessControl {
68
+ contract OwnableToAccessControlAdapter is AccessControlCore {
67
69
  /**
68
70
  * @dev Target OZ Ownable contract AccessControl Adapter executes the transactions on
69
71
  *
@@ -111,7 +113,7 @@ contract OwnableToAccessControlAdapter is AccessControl {
111
113
  * @param _target target OZ Ownable contract address
112
114
  * @param _owner smart contract owner having full privileges
113
115
  */
114
- constructor(address _target, address _owner) public AccessControl(_owner, 0) {
116
+ constructor(address _target, address _owner) AccessControlCore(_owner, 0) { // visibility modifier is required to be compilable with 0.6.x
115
117
  // verify the inputs
116
118
  require(_target != address(0), "zero address");
117
119
 
@@ -148,7 +150,7 @@ contract OwnableToAccessControlAdapter is AccessControl {
148
150
  */
149
151
  function updateAccessRole(bytes4 selector, uint256 role) public {
150
152
  // verify the access permission
151
- require(isSenderInRole(ROLE_ACCESS_ROLES_MANAGER), "access denied");
153
+ _requireSenderInRole(ROLE_ACCESS_ROLES_MANAGER);
152
154
 
153
155
  // update the function access role
154
156
  accessRoles[selector] = role;
@@ -190,7 +192,7 @@ contract OwnableToAccessControlAdapter is AccessControl {
190
192
  require(roleRequired != 0, "access role not set");
191
193
 
192
194
  // verify the access permission
193
- require(isSenderInRole(roleRequired), "access denied");
195
+ _requireSenderInRole(roleRequired);
194
196
  }
195
197
 
196
198
  // execute the call on the target
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity >=0.4.22;
2
+ pragma solidity >=0.8.4;
3
3
 
4
4
  import "../AccessControl.sol";
5
5
 
@@ -11,4 +11,10 @@ contract AccessControlMock is AccessControl {
11
11
  function restricted() public restrictedTo(RESTRICTED_ROLE) {
12
12
  emit Restricted();
13
13
  }
14
+ function requireSenderInRole(uint256 required) public view {
15
+ _requireSenderInRole(required);
16
+ }
17
+ function requireAccessCondition(bool condition) public pure {
18
+ _requireAccessCondition(condition);
19
+ }
14
20
  }
package/hardhat.config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * default Hardhat configuration which uses account mnemonic to derive accounts
3
- * script expects following environment variables to be set:
3
+ * script supports the following environment variables set:
4
4
  * - P_KEY1 – mainnet private key, should start with 0x
5
5
  * or
6
6
  * - MNEMONIC1 – mainnet mnemonic, 12 words
@@ -53,6 +53,10 @@
53
53
  * or
54
54
  * - MNEMONIC84532 – Base Sepolia (testnet) Optimistic Rollup (L2) mnemonic, 12 words
55
55
  *
56
+ * - P_KEY – Custom network (defined by its JSON-RPC) private key, should start with 0x
57
+ * or
58
+ * - MNEMONIC – Custom network (defined by its JSON-RPC) mnemonic, 12 words
59
+ *
56
60
  * - ALCHEMY_KEY – Alchemy API key
57
61
  * or
58
62
  * - INFURA_KEY – Infura API key (Project ID)
@@ -85,6 +89,13 @@ require("hardhat-gas-reporter");
85
89
  // https://www.npmjs.com/package/hardhat-deploy
86
90
  require("hardhat-deploy");
87
91
 
92
+ // automatically generate TypeScript bindings for smart contracts while using Hardhat
93
+ // TypeScript bindings help IDEs to properly recognize compiled contracts' ABIs
94
+ // https://github.com/dethcrypto/TypeChain/tree/master/packages/hardhat
95
+ // npm install -D typechain @typechain/hardhat @typechain/truffle-v5
96
+ // run: npx hardhat typechain
97
+ // require("@typechain/hardhat");
98
+
88
99
  // verify environment setup, display warning if required, replace missing values with fakes
89
100
  const FAKE_MNEMONIC = "test test test test test test test test test test test junk";
90
101
  if(!process.env.MNEMONIC1 && !process.env.P_KEY1) {
@@ -185,19 +196,26 @@ else if(process.env.P_KEY84531 && !process.env.P_KEY84531.startsWith("0x")) {
185
196
  }
186
197
  if(!process.env.MNEMONIC84532 && !process.env.P_KEY84532) {
187
198
  console.warn("neither MNEMONIC84532 nor P_KEY84532 is not set. Base Sepolia (testnet) deployments won't be available");
188
- process.env.MNEMONIC84531 = FAKE_MNEMONIC;
199
+ process.env.MNEMONIC84532 = FAKE_MNEMONIC;
189
200
  }
190
201
  else if(process.env.P_KEY84532 && !process.env.P_KEY84532.startsWith("0x")) {
191
202
  console.warn("P_KEY84532 doesn't start with 0x. Appended 0x");
192
203
  process.env.P_KEY84532 = "0x" + process.env.P_KEY84532;
193
204
  }
205
+ if(!process.env.MNEMONIC && !process.env.P_KEY) {
206
+ process.env.MNEMONIC = FAKE_MNEMONIC;
207
+ }
208
+ else if(process.env.P_KEY && !process.env.P_KEY.startsWith("0x")) {
209
+ console.warn("P_KEY doesn't start with 0x. Appended 0x");
210
+ process.env.P_KEY = "0x" + process.env.P_KEY;
211
+ }
194
212
  if(!process.env.INFURA_KEY && !process.env.ALCHEMY_KEY) {
195
213
  console.warn("neither INFURA_KEY nor ALCHEMY_KEY is not set. Deployments may not be available");
196
214
  process.env.INFURA_KEY = "";
197
215
  process.env.ALCHEMY_KEY = "";
198
216
  }
199
217
  if(!process.env.ETHERSCAN_KEY) {
200
- console.warn("ETHERSCAN_KEY is not set. Deployed smart contract code verification won't be available");
218
+ console.warn("ETHERSCAN_KEY is not set. Deployed smart contract code verification won't be available on etherscan");
201
219
  process.env.ETHERSCAN_KEY = "";
202
220
  }
203
221
  if(!process.env.POLYSCAN_KEY) {
@@ -210,6 +228,7 @@ if(!process.env.BSCSCAN_KEY) {
210
228
  }
211
229
  if(!process.env.BASESCAN_KEY) {
212
230
  console.warn("BASESCAN_KEY is not set. Deployed smart contract code verification won't be available on BaseScan");
231
+ process.env.BASESCAN_KEY = "";
213
232
  }
214
233
 
215
234
  /**
@@ -303,6 +322,10 @@ module.exports = {
303
322
  url: get_endpoint_url("base_sepolia"),
304
323
  accounts: get_accounts(process.env.P_KEY84532, process.env.MNEMONIC84532),
305
324
  },
325
+ custom: {
326
+ url: get_endpoint_url(),
327
+ accounts: get_accounts(process.env.P_KEY, process.env.MNEMONIC),
328
+ },
306
329
  },
307
330
 
308
331
  // Configure Solidity compiler
@@ -348,6 +371,12 @@ module.exports = {
348
371
  ]
349
372
  },
350
373
 
374
+ // configure typechain to generate Truffle v5 bindings
375
+ typechain: {
376
+ outDir: "typechain",
377
+ target: "truffle-v5",
378
+ },
379
+
351
380
  // Set default mocha options here, use special reporters etc.
352
381
  mocha: {
353
382
  // timeout: 100000,
@@ -424,12 +453,12 @@ function get_endpoint_url(network_name) {
424
453
  // create a key: https://www.alchemy.com/
425
454
  if(process.env.ALCHEMY_KEY) {
426
455
  switch(network_name) {
427
- case "mainnet": return "https://eth-mainnet.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
428
- case "ropsten": return "https://eth-ropsten.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
429
- case "rinkeby": return "https://eth-rinkeby.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
430
- case "kovan": return "https://eth-kovan.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
431
- case "goerli": return "https://eth-goerli.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
432
- case "sepolia": return "https://eth-sepolia.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
456
+ case "mainnet": return "https://eth-mainnet.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
457
+ case "ropsten": return "https://eth-ropsten.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
458
+ case "rinkeby": return "https://eth-rinkeby.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
459
+ case "kovan": return "https://eth-kovan.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
460
+ case "goerli": return "https://eth-goerli.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
461
+ case "sepolia": return "https://eth-sepolia.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
433
462
  case "polygon": return "https://polygon-mainnet.g.alchemy.com/v2/" + process.env.ALCHEMY_KEY;
434
463
  case "mumbai": return "https://polygon-mumbai.g.alchemy.com/v2/" + process.env.ALCHEMY_KEY;
435
464
  case "base_mainnet": return "https://base-mainnet.g.alchemy.com/v2/" + process.env.ALCHEMY_KEY;
package/index.js CHANGED
@@ -9,6 +9,9 @@ const {
9
9
  not,
10
10
  } = require("./scripts/include/features_roles");
11
11
 
12
+ // RBAC behaviours
13
+ const {behavesLikeRBAC} = require("./test/include/rbac.behaviour");
14
+
12
15
  // Re-export the functions
13
16
  module.exports = {
14
17
  ROLE_ACCESS_MANAGER,
@@ -16,4 +19,5 @@ module.exports = {
16
19
  FULL_PRIVILEGES_MASK,
17
20
  or,
18
21
  not,
22
+ behavesLikeRBAC,
19
23
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazy-sol/access-control",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
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,18 +22,22 @@
22
22
  "author": "Basil Gorin",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@lazy-sol/a-missing-gem": "^1.0.4",
25
+ "@lazy-sol/a-missing-gem": "^1.0.9",
26
26
  "@nomiclabs/hardhat-truffle5": "^2.0.7",
27
- "hardhat": "^2.22.4",
27
+ "hardhat": "^2.22.9",
28
28
  "hardhat-deploy": "^0.11.45"
29
29
  },
30
30
  "devDependencies": {
31
- "@lazy-sol/zeppelin-test-helpers": "^1.0.0",
31
+ "@lazy-sol/zeppelin-test-helpers": "^1.0.5",
32
32
  "hardhat-gas-reporter": "^1.0.10",
33
- "solidity-coverage": "^0.8.12"
33
+ "solidity-coverage": "^0.8.13"
34
34
  },
35
35
  "overrides": {
36
+ "axios": ">=1.7.5",
37
+ "micromatch": "^4.0.8",
38
+ "tar": "^6.2.1",
36
39
  "tough-cookie": "^4.1.3",
37
- "yargs-parser": "^5.0.1"
40
+ "yargs-parser": "^5.0.1",
41
+ "ws": "^8.0.0"
38
42
  }
39
43
  }
@@ -15,16 +15,17 @@ const {
15
15
  MAX_UINT256,
16
16
  } = constants;
17
17
 
18
- // BN utils
18
+ // BN constants and utilities
19
19
  const {
20
20
  random_bn255,
21
21
  random_bn256,
22
- } = require("@lazy-sol/a-missing-gem/bn_utils");
22
+ } = require("@lazy-sol/a-missing-gem");
23
23
 
24
24
  // RBAC core features and roles
25
25
  const {
26
26
  not,
27
- ROLE_ACCESS_MANAGER, FULL_PRIVILEGES_MASK,
27
+ ROLE_ACCESS_MANAGER,
28
+ FULL_PRIVILEGES_MASK,
28
29
  } = require("../../scripts/include/features_roles");
29
30
 
30
31
  /**
@@ -40,6 +41,18 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
40
41
  const by = a1;
41
42
  const to = a2;
42
43
 
44
+ describe("requireAccessCondition(condition) pure function", function() {
45
+ let access_control;
46
+ beforeEach(async function() {
47
+ access_control = await deployment_fn.call(this, a0, ZERO_ADDRESS, 0);
48
+ });
49
+ it("throws if condition is false", async function() {
50
+ await expectRevert(access_control.requireAccessCondition(false, {from: a0}), "AccessDenied()");
51
+ });
52
+ it("succeeds if condition is true", async function() {
53
+ await access_control.requireAccessCondition(true, {from: a0});
54
+ });
55
+ });
43
56
  describe("deployment and initial state", function() {
44
57
  function deploy_and_check(owner, features) {
45
58
  let access_control;
@@ -81,7 +94,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
81
94
  deploy_and_check(a1, random_bn256());
82
95
  });
83
96
  });
84
- describe("when deployed with not initial features", function() {
97
+ describe("when deployed with no initial features", function() {
85
98
  let access_control;
86
99
  beforeEach(async function() {
87
100
  access_control = await deployment_fn.call(this, a0);
@@ -92,7 +105,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
92
105
  beforeEach(async function() {
93
106
  await access_control.updateRole(by, ROLE_ACCESS_MANAGER, {from: a0});
94
107
  });
95
- describe("when ACCESS_MANAGER has full set of permissions", function() {
108
+ describe("when ACCESS_MANAGER has the full set of permissions", function() {
96
109
  beforeEach(async function() {
97
110
  await access_control.updateRole(by, MAX_UINT256, {from: a0});
98
111
  });
@@ -271,6 +284,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
271
284
  it("operator becomes an ACCESS_MANAGER", async function() {
272
285
  expect(await access_control.isOperatorInRole(to, ROLE_ACCESS_MANAGER), "operator").to.be.true;
273
286
  expect(await access_control.isSenderInRole(ROLE_ACCESS_MANAGER, {from: to}), "sender").to.be.true;
287
+ await access_control.requireSenderInRole(ROLE_ACCESS_MANAGER, {from: to});
274
288
  });
275
289
  });
276
290
  describe("when ACCESS_MANAGER revokes ACCESS_MANAGER permission from itself", function() {
@@ -280,15 +294,16 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
280
294
  it("operator ceases to be an ACCESS_MANAGER", async function() {
281
295
  expect(await access_control.isOperatorInRole(by, ROLE_ACCESS_MANAGER), "operator").to.be.false;
282
296
  expect(await access_control.isSenderInRole(ROLE_ACCESS_MANAGER, {from: by}), "sender").to.be.false;
297
+ await expectRevert(access_control.requireSenderInRole(ROLE_ACCESS_MANAGER, {from: to}), "AccessDenied()");
283
298
  });
284
299
  });
285
300
  });
286
301
  describe("otherwise (no ACCESS_MANAGER permission)", function() {
287
302
  it("updateFeatures reverts", async function() {
288
- await expectRevert(access_control.updateFeatures(1, {from: by}), "access denied");
303
+ await expectRevert(access_control.updateFeatures(1, {from: by}), "AccessDenied()");
289
304
  });
290
305
  it("updateRole reverts", async function() {
291
- await expectRevert(access_control.updateRole(to, 1, {from: by}), "access denied");
306
+ await expectRevert(access_control.updateRole(to, 1, {from: by}), "AccessDenied()");
292
307
  });
293
308
  });
294
309
  }
@@ -72,7 +72,7 @@ contract("OwnableToAccessControlAdapter tests", function(accounts) {
72
72
  from: a1,
73
73
  to: adapter.address,
74
74
  data: target.contract.methods.transferOwnership(a2).encodeABI(),
75
- }), "access denied");
75
+ }), "AccessDenied()");
76
76
  });
77
77
  describe("once an account has authorization to execute transferOwnership function", function() {
78
78
  beforeEach(async function() {
@@ -43,12 +43,12 @@ contract("OwnableToAccessControlAdapter: RBAC tests", function(accounts) {
43
43
  ({target, adapter} = await deploy_ownable_to_ac_adapter(a0));
44
44
  });
45
45
 
46
- it("it is impossible to configure the access role from unauthorized account", async function() {
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}), "access denied");
49
+ await expectRevert(adapter.updateAccessRole("1", 1, {from: operator}), "AccessDenied()");
50
50
  });
51
- it("it is possible to configure the access role from authorized account", async function() {
51
+ it("it is possible to configure the access role from the authorized account", async function() {
52
52
  const operator = a1;
53
53
  await adapter.updateRole(operator, ROLE_ACCESS_ROLES_MANAGER, {from: a0});
54
54
  adapter.updateAccessRole("1", 1, {from: operator});
@@ -38,7 +38,7 @@ contract('AccessControl (RBAC) "restrictedTo" Modifier tests', function(accounts
38
38
  access_control = await deploy_access_control(a0);
39
39
  });
40
40
  it("function protected with restrictedTo modifier fails when run not by an admin", async function() {
41
- await expectRevert(access_control.restricted({from: a1}), "access denied");
41
+ await expectRevert(access_control.restricted({from: a1}), "AccessDenied()");
42
42
  });
43
43
  describe("function protected with restrictedTo modifier succeeds when run by admin", async function() {
44
44
  let receipt;