@lazy-sol/access-control-upgradeable 1.1.0 → 1.1.2
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 +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
|
});
|