@lazy-sol/access-control-upgradeable 1.1.0 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,5 @@
1
+ v1.1.2: do not enable full privileges to zero address on contract initialization
2
+
1
3
  v1.1.0: Contact Size Optimizations
2
4
 
3
5
  - __Breaking Change:__ Solidity 0.8.4 is now required to compile the contracts (previously was 0.8.2).
package/README.md CHANGED
@@ -68,7 +68,7 @@ features and roles. Usually, smart contracts use different values for all the fe
68
68
  next section).
69
69
 
70
70
  Access manager may revoke its own permissions, including the bit 255. Eventually that allows an access manager to let
71
- the smart contract “float freely” and be controlled only by the community (via DAO) or by no one at all.
71
+ the smart contract “float freely” and be controlled only by the community (via the DAO) or by no one at all.
72
72
 
73
73
  ## Comparing with OpenZeppelin
74
74
 
@@ -76,7 +76,7 @@ Both our and OpenZeppelin Access Control implementations feature a similar API t
76
76
  thing".
77
77
 
78
78
  Zeppelin implementation is more flexible:
79
- * it allows setting unlimited number of roles, while current is limited to 256 different roles
79
+ * it allows setting an unlimited number of roles, while current is limited to 256 different roles
80
80
  * it allows setting an admin for each role, while current allows having only one global admin
81
81
 
82
82
  Our implementation is more lightweight:
@@ -120,11 +120,11 @@ npm i -D @lazy-sol/access-control-upgradeable
120
120
  Restricted function is a function with a `public` Solidity modifier access to which is restricted
121
121
  so that only a pre-configured set of accounts can execute it.
122
122
 
123
- 1. Enable role-based access control (RBAC) in a new smart contract
124
- by inheriting the RBAC contract from the [AccessControl](./contracts/UpgradeableAccessControl.sol) contract:
123
+ 1. Enable role-based access control (RBAC) in a new smart contract
124
+ by inheriting the RBAC contract from the [UpgradeableAccessControlCore](./contracts/UpgradeableAccessControlCore.sol) contract:
125
125
  ```solidity
126
126
  import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
127
- import "@lazy-sol/access-control/contracts/UpgradeableAccessControl.sol";
127
+ import "@lazy-sol/access-control/contracts/UpgradeableAccessControlCore.sol";
128
128
 
129
129
  /**
130
130
  * @title Simple ERC20 Implementation (Upgreadable)
@@ -133,29 +133,29 @@ so that only a pre-configured set of accounts can execute it.
133
133
  *
134
134
  * @author Lazy So[u]l
135
135
  */
136
- contract MyUpgradeableERC20Token is ERC20Upgradeable, UpgradeableAccessControl {
136
+ contract MyUpgradeableERC20Token is ERC20Upgradeable, UpgradeableAccessControlCore {
137
137
 
138
138
  ...
139
139
 
140
140
  }
141
141
  ```
142
142
 
143
- 2. Define an access control role with the unique integer value:
143
+ 2. Define an access control role with the unique integer value:
144
144
  ```solidity
145
145
  ...
146
146
 
147
147
  /**
148
148
  * @notice Token creator is responsible for creating (minting)
149
- tokens to an arbitrary address
149
+ * tokens to an arbitrary address
150
150
  * @dev Role ROLE_TOKEN_CREATOR allows minting tokens
151
- (calling `mint` function)
151
+ * (calling `mint` function)
152
152
  */
153
153
  uint32 public constant ROLE_TOKEN_CREATOR = 0x0001_0000;
154
154
 
155
155
  ...
156
156
  ```
157
157
 
158
- 3. Add the `require(isSenderInRole(ROLE_TOKEN_CREATOR), "access denied")"` check into the function body:
158
+ 3. Add the `_requireSenderInRole(ROLE_TOKEN_CREATOR)` check into the function body:
159
159
  ```solidity
160
160
  ...
161
161
 
@@ -164,7 +164,7 @@ so that only a pre-configured set of accounts can execute it.
164
164
  */
165
165
  function _mint(address _to, uint256 _value) internal virtual override {
166
166
  // check if caller has sufficient permissions to mint tokens
167
- require(isSenderInRole(ROLE_TOKEN_CREATOR), "access denied");
167
+ _requireSenderInRole(ROLE_TOKEN_CREATOR);
168
168
 
169
169
  // delegate to super implementation
170
170
  super._mint(_to, _value);
@@ -173,8 +173,8 @@ so that only a pre-configured set of accounts can execute it.
173
173
  ...
174
174
  ```
175
175
 
176
- Note: you could also use the `restrictedTo` modifier in the function declaration instead of the `require`
177
- in the function body if you don't need a custom error message:
176
+ Note: it is also possible to use the `restrictedTo` modifier in the function declaration instead of the `require`
177
+ in the function body if this better suits the coding style:
178
178
  ```solidity
179
179
  ...
180
180
 
@@ -189,6 +189,31 @@ so that only a pre-configured set of accounts can execute it.
189
189
  ...
190
190
  ```
191
191
 
192
+ ### Customizing the Error Message
193
+
194
+ Modifier `restrictedTo()`, internal functions `_requireSenderInRole()` and `_requireAccessCondition()` throw the
195
+ `AccessDenied` denied error when access check fails.
196
+
197
+ It is also possible to use your own custom errors or string messages if needed by leveraging a lower level boolean
198
+ functions `_isSenderInRole()` and `_isOperatorInRole()`:
199
+
200
+ ```solidity
201
+ ...
202
+
203
+ /**
204
+ * @inheritdoc ERC20Upgradeable
205
+ */
206
+ function _mint(address _to, uint256 _value) internal virtual override {
207
+ // check if caller has sufficient permissions to mint tokens
208
+ require(_isSenderInRole(ROLE_TOKEN_CREATOR), "access denied");
209
+
210
+ // delegate to super implementation
211
+ super._mint(_to, _value);
212
+ }
213
+
214
+ ...
215
+ ```
216
+
192
217
  Examples:
193
218
  [AdvancedERC20](https://raw.githubusercontent.com/lazy-sol/advanced-erc20/master/contracts/token/AdvancedERC20.sol),
194
219
  [ERC20v1](https://raw.githubusercontent.com/vgorin/solidity-template/master/contracts/token/upgradeable/ERC20v1.sol),
@@ -174,8 +174,11 @@ abstract contract InitializableAccessControlCore is Initializable {
174
174
  * @param _features initial features mask of the contract, can be zero
175
175
  */
176
176
  function _postConstruct(address _owner, uint256 _features) internal virtual onlyInitializing {
177
- // grant owner full privileges
178
- __setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
177
+ // if there is a request to set owner (zero address owner means no owner)
178
+ if(_owner != address(0)) {
179
+ // grant owner full privileges
180
+ __setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
181
+ }
179
182
  // update initial features bitmask
180
183
  __setRole(address(this), _features, _features);
181
184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazy-sol/access-control-upgradeable",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
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,15 +22,14 @@
22
22
  "author": "Basil Gorin",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@lazy-sol/a-missing-gem": "^1.0.9",
26
- "@nomiclabs/hardhat-truffle5": "^2.0.7",
27
- "@openzeppelin/contracts-upgradeable": "^4.9.6",
28
- "hardhat": "^2.22.9",
29
- "hardhat-deploy": "^0.11.45"
25
+ "@openzeppelin/contracts-upgradeable": "4.9.6"
30
26
  },
31
27
  "devDependencies": {
28
+ "@lazy-sol/a-missing-gem": "^1.0.11",
32
29
  "@lazy-sol/zeppelin-test-helpers": "^1.0.5",
33
- "@openzeppelin/contracts": "^4.9.6",
30
+ "@nomiclabs/hardhat-truffle5": "^2.0.7",
31
+ "@openzeppelin/contracts": "4.9.6",
32
+ "hardhat": "^2.22.13",
34
33
  "hardhat-dependency-injector": "^1.0.1",
35
34
  "hardhat-gas-reporter": "^1.0.10",
36
35
  "solidity-coverage": "^0.8.13"
@@ -59,13 +59,15 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
59
59
  beforeEach(async function() {
60
60
  access_control = await deployment_fn.call(this, a0, owner, features);
61
61
  });
62
- it('"RoleUpdated(owner)" event is emitted correctly', async function() {
63
- await expectEvent.inConstruction(access_control, "RoleUpdated", {
64
- operator: owner,
65
- requested: FULL_PRIVILEGES_MASK,
66
- assigned: FULL_PRIVILEGES_MASK,
62
+ if(owner !== ZERO_ADDRESS) {
63
+ it('"RoleUpdated(owner)" event is emitted correctly', async function() {
64
+ await expectEvent.inConstruction(access_control, "RoleUpdated", {
65
+ operator: owner,
66
+ requested: FULL_PRIVILEGES_MASK,
67
+ assigned: FULL_PRIVILEGES_MASK,
68
+ });
67
69
  });
68
- });
70
+ }
69
71
  it('"RoleUpdated(this)" event is emitted correctly', async function() {
70
72
  await expectEvent.inConstruction(access_control, "RoleUpdated", {
71
73
  operator: access_control.address,
@@ -73,9 +75,16 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
73
75
  assigned: features,
74
76
  });
75
77
  });
76
- it("owners' role is set correctly", async function() {
77
- expect(await access_control.getRole(owner)).to.be.bignumber.that.equals(FULL_PRIVILEGES_MASK);
78
- });
78
+ if(owner !== ZERO_ADDRESS) {
79
+ it("owners' role is set correctly", async function() {
80
+ expect(await access_control.getRole(owner)).to.be.bignumber.that.equals(FULL_PRIVILEGES_MASK);
81
+ });
82
+ }
83
+ else {
84
+ it("owners' role is not set", async function() {
85
+ expect(await access_control.getRole(owner)).to.be.bignumber.that.equals("0");
86
+ });
87
+ }
79
88
  it("features are set correctly", async function() {
80
89
  expect(await access_control.features()).to.be.bignumber.that.equals(features);
81
90
  });