@lazy-sol/access-control 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -4,6 +4,98 @@ 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
+
84
+ ## Upgradeability
85
+
86
+ [Upgradeable Access Control](https://github.com/lazy-sol/access-control-upgradeable) is a Role-based Access Control
87
+ extension supporting the OpenZeppelin UUPS Proxy upgrades. Smart contracts inheriting from the
88
+ `UpgradeableAccessControl` can be deployed behind the ERC1967 proxy and will get the upgradeability mechanism setup.
89
+
90
+ Upgradeable Access Control introduces another “special” permission bit 254 which is reserved for an upgrade manager role
91
+ `ROLE_UPGRADE_MANAGER` which is allowed to and is responsible for implementation upgrades of the ERC1967 Proxy.
92
+
93
+ Being controlled by the upgrade manager, the upgradeability is also a revocable feature of the smart contract: the
94
+ `upgradeTo` restricted function access can be revoked from all the admin accounts.
95
+
96
+ The best practice is to disable contract upgradeability when the protocol is mature enough and has already proven its
97
+ security and stability.
98
+
7
99
  ## Installation
8
100
  ```
9
101
  npm i -D @lazy-sol/access-control
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,
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.0.6",
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,13 +22,13 @@
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.6",
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.1",
32
32
  "hardhat-gas-reporter": "^1.0.10",
33
33
  "solidity-coverage": "^0.8.12"
34
34
  },