@lazy-sol/access-control-upgradeable 1.1.1 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/LICENSE.txt +1 -0
- package/README.md +21 -1
- package/audits/1.1_Prem_resolution.md +46 -0
- package/audits/1.1_final_Prem.pdf +0 -0
- package/contracts/InitializableAccessControl.sol +3 -3
- package/contracts/InitializableAccessControlCore.sol +53 -15
- package/contracts/UpgradeableAccessControlCore.sol +1 -1
- package/contracts/mocks/UpgradeableAccessControlMocks.sol +8 -0
- package/hardhat.config.js +1 -1
- package/package.json +6 -4
- package/test/include/rbac.behaviour.js +26 -9
- package/test/rbac_upgradeable.js +3 -0
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
v1.1.3: Prem's audit and its resolution
|
2
|
+
- See the list of issues found and resolved in [the audit resolution doc](./audits/1.1_Prem_resolution.md)
|
3
|
+
- See the audit methodology and issues found in [the original audit report](./audits/1.1_final_Prem.pdf)
|
4
|
+
|
5
|
+
v1.1.2: do not enable full privileges to zero address on contract initialization
|
6
|
+
|
1
7
|
v1.1.0: Contact Size Optimizations
|
2
8
|
|
3
9
|
- __Breaking Change:__ Solidity 0.8.4 is now required to compile the contracts (previously was 0.8.2).
|
package/LICENSE.txt
CHANGED
package/README.md
CHANGED
@@ -7,6 +7,10 @@ A shortcut to a modular and easily pluggable dapp architecture.
|
|
7
7
|
Enable the modular plug and play (PnP) architecture for your dapp by incorporating the role-based access control (RBAC)
|
8
8
|
into the smart contracts.
|
9
9
|
|
10
|
+
## Audit(s)
|
11
|
+
* [v1.1 Audit by Prem, January 1–30, 2025](./audits/1.1_final_Prem.pdf)
|
12
|
+
* [Resolution: v1.1 Audit by Prem](./audits/1.1_Prem_resolution.md)
|
13
|
+
|
10
14
|
## Technical Overview
|
11
15
|
|
12
16
|
Role-based Access Control (RBAC), or simply Access Control, is the base parent contract to be inherited by other smart
|
@@ -219,7 +223,23 @@ Examples:
|
|
219
223
|
[ERC20v1](https://raw.githubusercontent.com/vgorin/solidity-template/master/contracts/token/upgradeable/ERC20v1.sol),
|
220
224
|
[ERC721v1](https://raw.githubusercontent.com/vgorin/solidity-template/master/contracts/token/upgradeable/ERC721v1.sol).
|
221
225
|
|
226
|
+
## Evaluating Currently Enabled Features and Roles on the Deployed Contract
|
227
|
+
|
228
|
+
1. To evaluate currently enabled features use
|
229
|
+
* `features()` function, or
|
230
|
+
* `getRole(this)` function, replacing `this` with the deployed contract address
|
231
|
+
2. To evaluate currently enabled permissions for a **particular** address use
|
232
|
+
* `getRole(address)` function
|
233
|
+
3. To find **all** the addresses having any permissions, track the `RoleUpdated()` event and evaluate the history
|
234
|
+
of `assiged` roles for every `operator` address
|
235
|
+
* Alternatively, use the [tool](ui.html) which automates the process
|
236
|
+
(see demo [here](https://lazy-sol.github.io/access-control/ui.html))
|
237
|
+
|
222
238
|
## See Also
|
223
239
|
[Role-based Access Control (RBAC)](https://github.com/lazy-sol/access-control/blob/master/README.md)
|
224
240
|
|
225
|
-
|
241
|
+
## Contributing
|
242
|
+
Please see the [Contribution Guide](https://github.com/lazy-sol/access-control/blob/master/CONTRIBUTING.md) document to get understanding on how to report issues,
|
243
|
+
contribute to the source code, fix bugs, introduce new features, etc.
|
244
|
+
|
245
|
+
(c) 2017–2025 Basil Gorin
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# AccessControlUpgradable: Smart Contract Audit Report Resolution #
|
2
|
+
|
3
|
+
## Resolution Summary ##
|
4
|
+
|
5
|
+
| ID | | Resolution |
|
6
|
+
|---------|-------------------------------------------------------------|--------------|
|
7
|
+
| Minor-1 | Insufficient Event Logging in `RoleUpdated` | Fixed |
|
8
|
+
| Minor-2 | No Check for `role != 0` in `updateRole` | Mitigated |
|
9
|
+
| Notes-1 | Version Constraints with Known Issues | Mitigated |
|
10
|
+
| Notes-2 | Visibility Optimization for Gas Savings | Fixed |
|
11
|
+
| Notes-3 | Potential Improvement for `FULL_PRIVILEGES_MASK` Assignment | Acknowledged |
|
12
|
+
| Notes-4 | Test Coverage | Fixed |
|
13
|
+
|
14
|
+
For issues which were ignored, acknowledged, mitigated, or fixed differently than suggested by the auditor, see the
|
15
|
+
[Comments](#comments) section below.
|
16
|
+
|
17
|
+
## Comments ##
|
18
|
+
|
19
|
+
### Minor-2. No Check for `role != 0` in `updateRole` ###
|
20
|
+
To be used as a parent contract for RBAC-based applications, the `AccessControl` contract is originally designed to be
|
21
|
+
lightweight, which was improved even further in version 1.1 by introducing the `AccessControlCore` contract.
|
22
|
+
|
23
|
+
To minimise contract size, we minimise the number of public functions exposed and use a single public function
|
24
|
+
`updateRole` to add, modify, and delete permissions, including self-revoke.
|
25
|
+
|
26
|
+
Mitigated by adding an explicit and very well noticeable comment in the SolDoc for `updateRole` function.
|
27
|
+
|
28
|
+
### Notes-1. Version Constraints with Known Issues ###
|
29
|
+
To allow the use as a parent contract for a wide range of RBAC-based applications, and serve as a Solidity library,
|
30
|
+
we try to keep pragma constraint as low as possible. This approach maximizes compatibility.
|
31
|
+
|
32
|
+
Mitigated by updating the compiler version to 0.8.28 in `hardhat.config.js`.
|
33
|
+
|
34
|
+
### Notes-2. Visibility Optimization for Gas Savings
|
35
|
+
Visibility modifier was changed from `public` to `external` for functions `isFeatureEnabled`, `isSenderInRole`,
|
36
|
+
`isOperatorInRole`, `updateFeatures`, `updateRole`, `updateAccessRole`, and `deployNewOwnableToAccessControlAdapter`.
|
37
|
+
|
38
|
+
Functions `features`, and `getRole` remain public to be accessible in inheriting contracts.
|
39
|
+
|
40
|
+
### Notes-3. Potential Improvement for `FULL_PRIVILEGES_MASK` Assignment ###
|
41
|
+
Role-based Access Control (RBAC) library, and its AccessControl* contracts are designed to be a long-term, but still
|
42
|
+
temporary solution for the projects evolving in the direction of the fully decentralized operation. RBAC Lifecycle
|
43
|
+
assumes that all the permissions are eventually either fully revoked from external participants, or are fully
|
44
|
+
transitioned to the DAO governance smart contract.
|
45
|
+
|
46
|
+
Thus, further improvements to `FULL_PRIVILEGES_MASK` assignments and management are out of scope for the RBAC library.
|
Binary file
|
@@ -67,7 +67,7 @@ abstract contract InitializableAccessControl is InitializableAccessControlCore {
|
|
67
67
|
* @param required set of features to check against
|
68
68
|
* @return true if all the features requested are enabled, false otherwise
|
69
69
|
*/
|
70
|
-
function isFeatureEnabled(uint256 required)
|
70
|
+
function isFeatureEnabled(uint256 required) external view returns (bool) {
|
71
71
|
// delegate to internal `_isFeatureEnabled`
|
72
72
|
return _isFeatureEnabled(required);
|
73
73
|
}
|
@@ -78,7 +78,7 @@ abstract contract InitializableAccessControl is InitializableAccessControlCore {
|
|
78
78
|
* @param required set of permissions (role) to check against
|
79
79
|
* @return true if all the permissions requested are enabled, false otherwise
|
80
80
|
*/
|
81
|
-
function isSenderInRole(uint256 required)
|
81
|
+
function isSenderInRole(uint256 required) external view returns (bool) {
|
82
82
|
// delegate to internal `_isSenderInRole`
|
83
83
|
return _isSenderInRole(required);
|
84
84
|
}
|
@@ -90,7 +90,7 @@ abstract contract InitializableAccessControl is InitializableAccessControlCore {
|
|
90
90
|
* @param required set of permissions (role) to check
|
91
91
|
* @return true if all the permissions requested are enabled, false otherwise
|
92
92
|
*/
|
93
|
-
function isOperatorInRole(address operator, uint256 required)
|
93
|
+
function isOperatorInRole(address operator, uint256 required) external view returns (bool) {
|
94
94
|
// delegate to internal `_isOperatorInRole`
|
95
95
|
return _isOperatorInRole(operator, required);
|
96
96
|
}
|
@@ -130,11 +130,12 @@ abstract contract InitializableAccessControlCore is Initializable {
|
|
130
130
|
/**
|
131
131
|
* @dev Fired in updateRole() and updateFeatures()
|
132
132
|
*
|
133
|
+
* @param by address which has granted/revoked permissions to operator
|
133
134
|
* @param operator address which was granted/revoked permissions
|
134
135
|
* @param requested permissions requested
|
135
136
|
* @param assigned permissions effectively set
|
136
137
|
*/
|
137
|
-
event RoleUpdated(address indexed operator, uint256 requested, uint256 assigned);
|
138
|
+
event RoleUpdated(address indexed by, address indexed operator, uint256 requested, uint256 assigned);
|
138
139
|
|
139
140
|
/**
|
140
141
|
* @notice Function modifier making a function defined as public behave as restricted
|
@@ -174,8 +175,11 @@ abstract contract InitializableAccessControlCore is Initializable {
|
|
174
175
|
* @param _features initial features mask of the contract, can be zero
|
175
176
|
*/
|
176
177
|
function _postConstruct(address _owner, uint256 _features) internal virtual onlyInitializing {
|
177
|
-
//
|
178
|
-
|
178
|
+
// if there is a request to set owner (zero address owner means no owner)
|
179
|
+
if(_owner != address(0)) {
|
180
|
+
// grant owner full privileges
|
181
|
+
__setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
|
182
|
+
}
|
179
183
|
// update initial features bitmask
|
180
184
|
__setRole(address(this), _features, _features);
|
181
185
|
}
|
@@ -199,7 +203,7 @@ abstract contract InitializableAccessControlCore is Initializable {
|
|
199
203
|
*
|
200
204
|
* @return 256-bit bitmask of the features enabled
|
201
205
|
*/
|
202
|
-
function features() public view returns
|
206
|
+
function features() public view returns(uint256) {
|
203
207
|
// features are stored in 'this' address mapping of `userRoles`
|
204
208
|
return getRole(address(this));
|
205
209
|
}
|
@@ -213,9 +217,9 @@ abstract contract InitializableAccessControlCore is Initializable {
|
|
213
217
|
*
|
214
218
|
* @param _mask bitmask representing a set of features to enable/disable
|
215
219
|
*/
|
216
|
-
function updateFeatures(uint256 _mask)
|
217
|
-
// delegate
|
218
|
-
|
220
|
+
function updateFeatures(uint256 _mask) external {
|
221
|
+
// delegate to internal `_updateRole()`
|
222
|
+
_updateRole(address(this), _mask);
|
219
223
|
}
|
220
224
|
|
221
225
|
/**
|
@@ -240,22 +244,34 @@ abstract contract InitializableAccessControlCore is Initializable {
|
|
240
244
|
* @notice Updates set of permissions (role) for a given user,
|
241
245
|
* taking into account sender's permissions.
|
242
246
|
*
|
243
|
-
* @dev Setting role to zero is equivalent to removing
|
247
|
+
* @dev Setting role to zero is equivalent to removing all the permissions
|
244
248
|
* @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
|
245
249
|
* copying senders' permissions (role) to the user
|
246
250
|
* @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
|
247
251
|
*
|
252
|
+
* ╔════════════════════════════════════════════════════════════════════════╗
|
253
|
+
* ║ WARNING: RISK OF ACCIDENTAL SELF-REVOKE ║
|
254
|
+
* ╠════════════════════════════════════════════════════════════════════════╣
|
255
|
+
* ║ updateRole function is used to add, update, delete permissions, to ║
|
256
|
+
* ║ revoke and self-revoke all the permissions, as well as to disable ║
|
257
|
+
* ║ contract upgradability by revoking ROLE_UPGRADE_MANAGER permission ║
|
258
|
+
* ║ ║
|
259
|
+
* ║ updateRole(msg.sender, 0) executed by a super admin themselves ║
|
260
|
+
* ║ revokes super admin permissions forever if there is no other super ║
|
261
|
+
* ║ admin set prior to updateRole(msg.sender, 0) call ║
|
262
|
+
* ║ ║
|
263
|
+
* ║ Note that in such a case upgradeable contracts also stop being ║
|
264
|
+
* ║ upgradable as ROLE_UPGRADE_MANAGER permission is revoked ║
|
265
|
+
* ╚════════════════════════════════════════════════════════════════════════╝
|
266
|
+
*
|
248
267
|
* @param operator address of a user to alter permissions for,
|
249
268
|
* or self address to alter global features of the smart contract
|
250
269
|
* @param role bitmask representing a set of permissions to
|
251
270
|
* enable/disable for a user specified
|
252
271
|
*/
|
253
|
-
function updateRole(address operator, uint256 role)
|
254
|
-
//
|
255
|
-
|
256
|
-
|
257
|
-
// evaluate the role and reassign it
|
258
|
-
__setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
|
272
|
+
function updateRole(address operator, uint256 role) external {
|
273
|
+
// delegate to internal `_updateRole()`
|
274
|
+
_updateRole(operator, role);
|
259
275
|
}
|
260
276
|
|
261
277
|
/**
|
@@ -367,6 +383,28 @@ abstract contract InitializableAccessControlCore is Initializable {
|
|
367
383
|
return __hasRole(getRole(operator), required);
|
368
384
|
}
|
369
385
|
|
386
|
+
/**
|
387
|
+
* @dev Updates set of permissions (role) for a given user,
|
388
|
+
* taking into account sender's permissions.
|
389
|
+
*
|
390
|
+
* @dev Setting role to zero is equivalent to removing all the permissions
|
391
|
+
* @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
|
392
|
+
* copying senders' permissions (role) to the user
|
393
|
+
* @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
|
394
|
+
*
|
395
|
+
* @param operator address of a user to alter permissions for,
|
396
|
+
* or self address to alter global features of the smart contract
|
397
|
+
* @param role bitmask representing a set of permissions to
|
398
|
+
* enable/disable for a user specified
|
399
|
+
*/
|
400
|
+
function _updateRole(address operator, uint256 role) internal {
|
401
|
+
// caller must have a permission to update user roles
|
402
|
+
_requireSenderInRole(ROLE_ACCESS_MANAGER);
|
403
|
+
|
404
|
+
// evaluate the role and reassign it
|
405
|
+
__setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
|
406
|
+
}
|
407
|
+
|
370
408
|
/**
|
371
409
|
* @dev Sets the `assignedRole` role to the operator, logs both `requestedRole` and `actualRole`
|
372
410
|
*
|
@@ -387,7 +425,7 @@ abstract contract InitializableAccessControlCore is Initializable {
|
|
387
425
|
userRoles[operator] = assignedRole;
|
388
426
|
|
389
427
|
// fire an event
|
390
|
-
emit RoleUpdated(operator, requestedRole, assignedRole);
|
428
|
+
emit RoleUpdated(msg.sender, operator, requestedRole, assignedRole);
|
391
429
|
}
|
392
430
|
|
393
431
|
/**
|
@@ -73,7 +73,7 @@ abstract contract UpgradeableAccessControlCore is InitializableAccessControlCore
|
|
73
73
|
*
|
74
74
|
* @return the current implementation address
|
75
75
|
*/
|
76
|
-
function getImplementation()
|
76
|
+
function getImplementation() external view virtual returns (address) {
|
77
77
|
// delegate to `ERC1967Upgrade._getImplementation()`
|
78
78
|
return _getImplementation();
|
79
79
|
}
|
@@ -22,6 +22,10 @@ contract UpgradeableAccessControl1 is UpgradeableAccessControlMock {
|
|
22
22
|
string public version1;
|
23
23
|
|
24
24
|
function postConstruct(address _owner, uint256 _features) public virtual initializer {
|
25
|
+
postConstructNonInit(_owner, _features);
|
26
|
+
}
|
27
|
+
|
28
|
+
function postConstructNonInit(address _owner, uint256 _features) public virtual {
|
25
29
|
_postConstruct(_owner, _features);
|
26
30
|
version1 = "1";
|
27
31
|
}
|
@@ -31,6 +35,10 @@ contract UpgradeableAccessControl2 is UpgradeableAccessControlMock {
|
|
31
35
|
string public version2;
|
32
36
|
|
33
37
|
function postConstruct(address _owner, uint256 _features) public virtual initializer {
|
38
|
+
postConstructNonInit(_owner, _features);
|
39
|
+
}
|
40
|
+
|
41
|
+
function postConstructNonInit(address _owner, uint256 _features) public virtual {
|
34
42
|
_postConstruct(_owner, _features);
|
35
43
|
version2 = "2";
|
36
44
|
}
|
package/hardhat.config.js
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lazy-sol/access-control-upgradeable",
|
3
|
-
"version": "1.1.
|
3
|
+
"version": "1.1.4",
|
4
4
|
"description": "Enable the modular plug and play (PnP) architecture for your dapp by incorporating the role-based access control (RBAC) into the smart contracts",
|
5
5
|
"main": "index.js",
|
6
6
|
"scripts": {
|
@@ -25,17 +25,19 @@
|
|
25
25
|
"@openzeppelin/contracts-upgradeable": "4.9.6"
|
26
26
|
},
|
27
27
|
"devDependencies": {
|
28
|
-
"@lazy-sol/a-missing-gem": "^1.0.
|
28
|
+
"@lazy-sol/a-missing-gem": "^1.0.12",
|
29
29
|
"@lazy-sol/zeppelin-test-helpers": "^1.0.5",
|
30
30
|
"@nomiclabs/hardhat-truffle5": "^2.0.7",
|
31
31
|
"@openzeppelin/contracts": "4.9.6",
|
32
|
-
"hardhat": "^2.
|
32
|
+
"hardhat": "^2.25.0",
|
33
33
|
"hardhat-dependency-injector": "^1.0.1",
|
34
34
|
"hardhat-gas-reporter": "^1.0.10",
|
35
|
-
"solidity-coverage": "^0.8.
|
35
|
+
"solidity-coverage": "^0.8.16"
|
36
36
|
},
|
37
37
|
"overrides": {
|
38
38
|
"axios": ">=1.7.5",
|
39
|
+
"cookie": ">=0.7.0",
|
40
|
+
"elliptic": "^6.6.0",
|
39
41
|
"micromatch": "^4.0.8",
|
40
42
|
"tar": "^6.2.1",
|
41
43
|
"tough-cookie": "^4.1.3",
|
@@ -59,23 +59,34 @@ 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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
if(owner !== ZERO_ADDRESS) {
|
63
|
+
it('"RoleUpdated(owner)" event is emitted correctly', async function() {
|
64
|
+
await expectEvent.inConstruction(access_control, "RoleUpdated", {
|
65
|
+
by: a0,
|
66
|
+
operator: owner,
|
67
|
+
requested: FULL_PRIVILEGES_MASK,
|
68
|
+
assigned: FULL_PRIVILEGES_MASK,
|
69
|
+
});
|
67
70
|
});
|
68
|
-
}
|
71
|
+
}
|
69
72
|
it('"RoleUpdated(this)" event is emitted correctly', async function() {
|
70
73
|
await expectEvent.inConstruction(access_control, "RoleUpdated", {
|
74
|
+
by: a0,
|
71
75
|
operator: access_control.address,
|
72
76
|
requested: features,
|
73
77
|
assigned: features,
|
74
78
|
});
|
75
79
|
});
|
76
|
-
|
77
|
-
|
78
|
-
|
80
|
+
if(owner !== ZERO_ADDRESS) {
|
81
|
+
it("owners' role is set correctly", async function() {
|
82
|
+
expect(await access_control.getRole(owner)).to.be.bignumber.that.equals(FULL_PRIVILEGES_MASK);
|
83
|
+
});
|
84
|
+
}
|
85
|
+
else {
|
86
|
+
it("owners' role is not set", async function() {
|
87
|
+
expect(await access_control.getRole(owner)).to.be.bignumber.that.equals("0");
|
88
|
+
});
|
89
|
+
}
|
79
90
|
it("features are set correctly", async function() {
|
80
91
|
expect(await access_control.features()).to.be.bignumber.that.equals(features);
|
81
92
|
});
|
@@ -125,6 +136,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
125
136
|
});
|
126
137
|
it('"RoleUpdated" event', async function() {
|
127
138
|
expectEvent(receipt, "RoleUpdated", {
|
139
|
+
by,
|
128
140
|
operator: to_fn(to),
|
129
141
|
requested: set,
|
130
142
|
assigned: set,
|
@@ -148,6 +160,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
148
160
|
});
|
149
161
|
it('"RoleUpdated" event', async function() {
|
150
162
|
expectEvent(receipt, "RoleUpdated", {
|
163
|
+
by,
|
151
164
|
operator: to_fn(to),
|
152
165
|
requested: not(remove),
|
153
166
|
assigned: not(remove),
|
@@ -173,6 +186,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
173
186
|
});
|
174
187
|
it('"RoleUpdated" event', async function() {
|
175
188
|
expectEvent(receipt, "RoleUpdated", {
|
189
|
+
by,
|
176
190
|
operator: to_fn(to),
|
177
191
|
requested: set,
|
178
192
|
assigned: "0",
|
@@ -197,6 +211,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
197
211
|
});
|
198
212
|
it('"RoleUpdated" event', async function() {
|
199
213
|
expectEvent(receipt, "RoleUpdated", {
|
214
|
+
by,
|
200
215
|
operator: to_fn(to),
|
201
216
|
requested: not(remove),
|
202
217
|
assigned: MAX_UINT256,
|
@@ -228,6 +243,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
228
243
|
});
|
229
244
|
it('"RoleUpdated" event', async function() {
|
230
245
|
expectEvent(receipt, "RoleUpdated", {
|
246
|
+
by,
|
231
247
|
operator: to_fn(to),
|
232
248
|
requested: set,
|
233
249
|
assigned: role.and(set),
|
@@ -252,6 +268,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
252
268
|
});
|
253
269
|
it('"RoleUpdated" event', async function() {
|
254
270
|
expectEvent(receipt, "RoleUpdated", {
|
271
|
+
by,
|
255
272
|
operator: to_fn(to),
|
256
273
|
requested: not(remove),
|
257
274
|
assigned: not(role.and(remove)),
|
package/test/rbac_upgradeable.js
CHANGED
@@ -54,6 +54,9 @@ contract("UpgradeableAccessControl (U-RBAC) Core tests", function(accounts) {
|
|
54
54
|
it("it is impossible to re-initialize", async function() {
|
55
55
|
await expectRevert(ac.postConstruct(ZERO_ADDRESS, 0, {from: a0}), "Initializable: contract is already initialized");
|
56
56
|
});
|
57
|
+
it("it is impossible to postConstruct non-initializing", async function() {
|
58
|
+
await expectRevert(ac.postConstructNonInit(ZERO_ADDRESS, 0, {from: a0}), "Initializable: contract is not initializing");
|
59
|
+
});
|
57
60
|
describe("when there is new (v2) implementation available", function() {
|
58
61
|
let impl2;
|
59
62
|
beforeEach(async function() {
|