@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 +2 -0
- package/README.md +38 -13
- package/contracts/InitializableAccessControlCore.sol +5 -2
- package/package.json +6 -7
- package/test/include/rbac.behaviour.js +18 -9
package/CHANGELOG.md
CHANGED
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.
|
124
|
-
|
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/
|
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,
|
136
|
+
contract MyUpgradeableERC20Token is ERC20Upgradeable, UpgradeableAccessControlCore {
|
137
137
|
|
138
138
|
...
|
139
139
|
|
140
140
|
}
|
141
141
|
```
|
142
142
|
|
143
|
-
2.
|
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.
|
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
|
-
|
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
|
-
|
177
|
-
|
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
|
-
//
|
178
|
-
|
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.
|
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
|
-
"@
|
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
|
-
"@
|
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
|
-
|
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
|
+
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
|
-
|
77
|
-
|
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
|
});
|