@lukso/lsp8-contracts 0.16.7 → 0.17.3
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/LICENSE +201 -0
- package/README.md +54 -4
- package/artifacts/IAccessControlExtended.json +285 -0
- package/artifacts/ILSP8CappedBalance.json +27 -0
- package/artifacts/ILSP8CappedSupply.json +27 -0
- package/artifacts/ILSP8IdentifiableDigitalAsset.json +6 -3
- package/artifacts/ILSP8Mintable.json +62 -0
- package/artifacts/ILSP8NonTransferable.json +110 -0
- package/artifacts/ILSP8Revokable.json +75 -0
- package/artifacts/LSP8Burnable.json +7 -4
- package/artifacts/LSP8BurnableInitAbstract.json +7 -4
- package/artifacts/LSP8CappedBalanceAbstract.json +1285 -0
- package/artifacts/LSP8CappedBalanceInitAbstract.json +1293 -0
- package/artifacts/{LSP8CappedSupply.json → LSP8CappedSupplyAbstract.json} +8 -15
- package/artifacts/LSP8CappedSupplyInitAbstract.json +7 -14
- package/artifacts/LSP8CustomizableToken.json +1738 -0
- package/artifacts/LSP8CustomizableTokenInit.json +1733 -0
- package/artifacts/LSP8Enumerable.json +7 -4
- package/artifacts/LSP8EnumerableInitAbstract.json +7 -4
- package/artifacts/LSP8IdentifiableDigitalAsset.json +6 -3
- package/artifacts/LSP8IdentifiableDigitalAssetInitAbstract.json +6 -3
- package/artifacts/LSP8Mintable.json +369 -5
- package/artifacts/LSP8MintableAbstract.json +1328 -0
- package/artifacts/LSP8MintableInit.json +369 -5
- package/artifacts/LSP8MintableInitAbstract.json +1336 -0
- package/artifacts/LSP8NonTransferableAbstract.json +1367 -0
- package/artifacts/LSP8NonTransferableInitAbstract.json +1375 -0
- package/artifacts/LSP8RevokableAbstract.json +1317 -0
- package/artifacts/LSP8RevokableInitAbstract.json +1325 -0
- package/artifacts/LSP8Votes.json +7 -4
- package/artifacts/LSP8VotesInitAbstract.json +7 -4
- package/contracts/ILSP8IdentifiableDigitalAsset.sol +1 -1
- package/contracts/LSP8Constants.sol +1 -1
- package/contracts/LSP8Errors.sol +1 -1
- package/contracts/LSP8IdentifiableDigitalAsset.sol +73 -114
- package/contracts/LSP8IdentifiableDigitalAssetInitAbstract.sol +69 -116
- package/contracts/extensions/AccessControlExtended/AccessControlExtendedAbstract.sol +378 -0
- package/contracts/extensions/AccessControlExtended/AccessControlExtendedConstants.sol +13 -0
- package/contracts/extensions/AccessControlExtended/AccessControlExtendedErrors.sol +23 -0
- package/contracts/extensions/AccessControlExtended/AccessControlExtendedInitAbstract.sol +390 -0
- package/contracts/extensions/AccessControlExtended/IAccessControlExtended.sol +51 -0
- package/contracts/extensions/{LSP8Burnable.sol → LSP8Burnable/LSP8Burnable.sol} +7 -6
- package/contracts/extensions/{LSP8BurnableInitAbstract.sol → LSP8Burnable/LSP8BurnableInitAbstract.sol} +7 -6
- package/contracts/extensions/LSP8CappedBalance/ILSP8CappedBalance.sol +11 -0
- package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceAbstract.sol +124 -0
- package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceErrors.sol +9 -0
- package/contracts/extensions/LSP8CappedBalance/LSP8CappedBalanceInitAbstract.sol +174 -0
- package/contracts/extensions/LSP8CappedSupply/ILSP8CappedSupply.sol +11 -0
- package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyAbstract.sol +59 -0
- package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyErrors.sol +6 -0
- package/contracts/extensions/LSP8CappedSupply/LSP8CappedSupplyInitAbstract.sol +97 -0
- package/contracts/extensions/{LSP8Enumerable.sol → LSP8Enumerable/LSP8Enumerable.sol} +2 -2
- package/contracts/extensions/{LSP8EnumerableInitAbstract.sol → LSP8Enumerable/LSP8EnumerableInitAbstract.sol} +2 -2
- package/contracts/extensions/LSP8Mintable/ILSP8Mintable.sol +27 -0
- package/contracts/extensions/LSP8Mintable/LSP8MintableAbstract.sol +105 -0
- package/contracts/extensions/LSP8Mintable/LSP8MintableErrors.sol +5 -0
- package/contracts/extensions/LSP8Mintable/LSP8MintableInitAbstract.sol +155 -0
- package/contracts/extensions/LSP8NonTransferable/ILSP8NonTransferable.sol +48 -0
- package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableAbstract.sol +190 -0
- package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableErrors.sol +14 -0
- package/contracts/extensions/LSP8NonTransferable/LSP8NonTransferableInitAbstract.sol +246 -0
- package/contracts/extensions/LSP8Revokable/ILSP8Revokable.sol +28 -0
- package/contracts/extensions/LSP8Revokable/LSP8RevokableAbstract.sol +132 -0
- package/contracts/extensions/LSP8Revokable/LSP8RevokableErrors.sol +4 -0
- package/contracts/extensions/LSP8Revokable/LSP8RevokableInitAbstract.sol +178 -0
- package/contracts/extensions/{LSP8Votes.sol → LSP8Votes/LSP8Votes.sol} +3 -4
- package/contracts/extensions/{LSP8VotesConstants.sol → LSP8Votes/LSP8VotesConstants.sol} +1 -1
- package/contracts/extensions/{LSP8VotesInitAbstract.sol → LSP8Votes/LSP8VotesInitAbstract.sol} +3 -3
- package/contracts/presets/LSP8CustomizableToken.sol +277 -0
- package/contracts/presets/LSP8CustomizableTokenConstants.sol +32 -0
- package/contracts/presets/LSP8CustomizableTokenInit.sol +318 -0
- package/contracts/presets/LSP8Mintable.sol +13 -28
- package/contracts/presets/LSP8MintableInit.sol +13 -6
- package/dist/abi.cjs +8233 -158
- package/dist/abi.d.cts +12004 -323
- package/dist/abi.d.mts +12004 -323
- package/dist/abi.d.ts +12004 -323
- package/dist/abi.mjs +8217 -158
- package/dist/constants.cjs +21 -0
- package/dist/constants.d.cts +12 -1
- package/dist/constants.d.mts +12 -1
- package/dist/constants.d.ts +12 -1
- package/dist/constants.mjs +16 -1
- package/package.json +32 -9
- package/contracts/extensions/LSP8CappedSupply.sol +0 -85
- package/contracts/extensions/LSP8CappedSupplyInitAbstract.sol +0 -88
- package/contracts/presets/ILSP8Mintable.sol +0 -33
- package/contracts/presets/LSP8MintableInitAbstract.sol +0 -62
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {
|
|
6
|
+
IAccessControl
|
|
7
|
+
} from "@openzeppelin/contracts/access/IAccessControl.sol";
|
|
8
|
+
import {
|
|
9
|
+
IAccessControlEnumerable
|
|
10
|
+
} from "@openzeppelin/contracts/access/IAccessControlEnumerable.sol";
|
|
11
|
+
import {IAccessControlExtended} from "./IAccessControlExtended.sol";
|
|
12
|
+
|
|
13
|
+
// modules
|
|
14
|
+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
15
|
+
|
|
16
|
+
// libraries
|
|
17
|
+
import {
|
|
18
|
+
EnumerableSet
|
|
19
|
+
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
|
20
|
+
|
|
21
|
+
// constants
|
|
22
|
+
import {
|
|
23
|
+
_INTERFACEID_ACCESSCONTROL,
|
|
24
|
+
_INTERFACEID_ACCESSCONTROLENUMERABLE,
|
|
25
|
+
_INTERFACEID_ACCESSCONTROLEXTENDED
|
|
26
|
+
} from "./AccessControlExtendedConstants.sol";
|
|
27
|
+
|
|
28
|
+
// errors
|
|
29
|
+
import {
|
|
30
|
+
AccessControlUnauthorizedAccount,
|
|
31
|
+
AccessControlBadConfirmation,
|
|
32
|
+
AccessControlCannotSetAdminForDefaultAdminRole
|
|
33
|
+
} from "./AccessControlExtendedErrors.sol";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @title AccessControlExtendedAbstract
|
|
37
|
+
* @dev Abstract contract implementing OZ-compatible role management with reverse lookups
|
|
38
|
+
* Uses EnumerableSet composition (NOT OZ AccessControl inheritance)
|
|
39
|
+
*
|
|
40
|
+
* Provides:
|
|
41
|
+
* - Standard OZ {IAccessControl} functions: hasRole, grantRole, revokeRole, renounceRole, getRoleAdmin
|
|
42
|
+
* - Standard OZ {IAccessControlEnumerable} functions: getRoleMember, getRoleMemberCount
|
|
43
|
+
* - Extended functions: rolesOf, getRoleMembers
|
|
44
|
+
* - Explicit role checks for every role-gated function
|
|
45
|
+
* - DEFAULT_ADMIN_ROLE as root admin for granting and revoking roles
|
|
46
|
+
* - Automatic transfer of all roles between old and new on ownership transfer
|
|
47
|
+
*/
|
|
48
|
+
abstract contract AccessControlExtendedAbstract is
|
|
49
|
+
IAccessControlExtended,
|
|
50
|
+
Ownable
|
|
51
|
+
{
|
|
52
|
+
using EnumerableSet for EnumerableSet.AddressSet;
|
|
53
|
+
using EnumerableSet for EnumerableSet.Bytes32Set;
|
|
54
|
+
|
|
55
|
+
// --- Constants
|
|
56
|
+
|
|
57
|
+
/// @dev The default admin role. Value is `bytes32(0)` per OZ convention.
|
|
58
|
+
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
|
|
59
|
+
|
|
60
|
+
// --- Storage
|
|
61
|
+
|
|
62
|
+
/// @dev Mapping from role to its admin role.
|
|
63
|
+
mapping(bytes32 role => bytes32 adminRole) private _roleAdmins;
|
|
64
|
+
|
|
65
|
+
/// @dev Forward lookup: role -> set of member addresses.
|
|
66
|
+
mapping(bytes32 role => EnumerableSet.AddressSet members)
|
|
67
|
+
private _roleMembers;
|
|
68
|
+
|
|
69
|
+
/// @dev Reverse lookup: address -> set of roles held.
|
|
70
|
+
mapping(address account => EnumerableSet.Bytes32Set rolesAssigned)
|
|
71
|
+
private _addressRoles;
|
|
72
|
+
|
|
73
|
+
// --- Modifier
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @dev Modifier that checks the caller has `role`.
|
|
77
|
+
* Reverts with {AccessControlUnauthorizedAccount} if the check fails.
|
|
78
|
+
*/
|
|
79
|
+
modifier onlyRole(bytes32 role) {
|
|
80
|
+
_checkRole(role);
|
|
81
|
+
_;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// --- Constructor
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @dev Grants `DEFAULT_ADMIN_ROLE` to the contract owner so to allow it to administer roles to other addresses after deployment.
|
|
88
|
+
* This will also make it appear inside the enumerations (getRoleMember, rolesOf, getRoleMembers).
|
|
89
|
+
*/
|
|
90
|
+
constructor() {
|
|
91
|
+
_grantRole(DEFAULT_ADMIN_ROLE, owner());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// --- ERC-165
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @dev Returns true for {IAccessControl}, {IAccessControlEnumerable} and {IAccessControlExtended}.
|
|
98
|
+
*/
|
|
99
|
+
function supportsInterface(
|
|
100
|
+
bytes4 interfaceId
|
|
101
|
+
) public view virtual returns (bool) {
|
|
102
|
+
return
|
|
103
|
+
interfaceId == _INTERFACEID_ACCESSCONTROL ||
|
|
104
|
+
interfaceId == _INTERFACEID_ACCESSCONTROLENUMERABLE ||
|
|
105
|
+
interfaceId == _INTERFACEID_ACCESSCONTROLEXTENDED;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --- IAccessControl
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @inheritdoc IAccessControl
|
|
112
|
+
*/
|
|
113
|
+
function hasRole(
|
|
114
|
+
bytes32 role,
|
|
115
|
+
address account
|
|
116
|
+
) public view virtual returns (bool) {
|
|
117
|
+
return _hasRole(role, account);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @inheritdoc IAccessControl
|
|
122
|
+
*/
|
|
123
|
+
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
|
|
124
|
+
return _roleAdmins[role];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @inheritdoc IAccessControlExtended
|
|
129
|
+
*
|
|
130
|
+
* @dev Sets `adminRole` as the admin of `role`. Available for extensions to configure custom admin hierarchies.
|
|
131
|
+
*
|
|
132
|
+
* @custom:warning
|
|
133
|
+
* - DO NOT expose this function without `onlyOwner` or `onlyRole(DEFAULT_ADMIN_ROLE)` access control.
|
|
134
|
+
* - Be aware that calling `setRoleAdmin(X, X)` creates a self-admin where nobody can grant role `X` unless someone already holds role `X`.
|
|
135
|
+
*
|
|
136
|
+
* @custom:requirements
|
|
137
|
+
* - `role` cannot be the `DEFAULT_ADMIN_ROLE`.
|
|
138
|
+
* - The caller must hold the `DEFAULT_ADMIN_ROLE`.
|
|
139
|
+
*
|
|
140
|
+
* @custom:events {RoleAdminChanged} with the previous and new admin roles.
|
|
141
|
+
*/
|
|
142
|
+
function setRoleAdmin(
|
|
143
|
+
bytes32 role,
|
|
144
|
+
bytes32 adminRole
|
|
145
|
+
) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
|
|
146
|
+
_setRoleAdmin(role, adminRole);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @inheritdoc IAccessControl
|
|
151
|
+
* @dev Grants `role` to `account`.
|
|
152
|
+
*
|
|
153
|
+
* @custom:requirements The caller must hold the admin role for `role`.
|
|
154
|
+
*/
|
|
155
|
+
function grantRole(
|
|
156
|
+
bytes32 role,
|
|
157
|
+
address account
|
|
158
|
+
) public virtual onlyRole(getRoleAdmin(role)) {
|
|
159
|
+
_grantRole(role, account);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @inheritdoc IAccessControl
|
|
164
|
+
* @dev Revokes `role` from `account`. The caller must hold the admin role for `role`.
|
|
165
|
+
*
|
|
166
|
+
* @custom:warning `DEFAULT_ADMIN_ROLE` cannot be removed from the current owner to prevent lockout.
|
|
167
|
+
*/
|
|
168
|
+
function revokeRole(
|
|
169
|
+
bytes32 role,
|
|
170
|
+
address account
|
|
171
|
+
) public virtual onlyRole(getRoleAdmin(role)) {
|
|
172
|
+
require(
|
|
173
|
+
!(role == DEFAULT_ADMIN_ROLE && account == owner()),
|
|
174
|
+
AccessControlUnauthorizedAccount(account, role)
|
|
175
|
+
);
|
|
176
|
+
_revokeRole(role, account);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @inheritdoc IAccessControl
|
|
181
|
+
* @dev Allows `msg.sender` to renounce their own `role`. The `callerConfirmation`
|
|
182
|
+
* parameter must equal `msg.sender` to prevent accidental renouncement (OZ pattern).
|
|
183
|
+
*
|
|
184
|
+
* @custom:warning The current owner cannot renounce `DEFAULT_ADMIN_ROLE`
|
|
185
|
+
* to prevent locking the contract out of role administration.
|
|
186
|
+
*
|
|
187
|
+
* @custom:events Emits {RoleRevoked} if `msg.sender` currently holds `role` and successfully revokes it for itself.
|
|
188
|
+
*/
|
|
189
|
+
function renounceRole(
|
|
190
|
+
bytes32 role,
|
|
191
|
+
address callerConfirmation
|
|
192
|
+
) public virtual {
|
|
193
|
+
require(
|
|
194
|
+
callerConfirmation == msg.sender,
|
|
195
|
+
AccessControlBadConfirmation()
|
|
196
|
+
);
|
|
197
|
+
require(
|
|
198
|
+
!(role == DEFAULT_ADMIN_ROLE && msg.sender == owner()),
|
|
199
|
+
AccessControlUnauthorizedAccount(msg.sender, role)
|
|
200
|
+
);
|
|
201
|
+
_revokeRole(role, msg.sender);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// --- IAccessControlEnumerable
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @inheritdoc IAccessControlEnumerable
|
|
208
|
+
*/
|
|
209
|
+
function getRoleMember(
|
|
210
|
+
bytes32 role,
|
|
211
|
+
uint256 index
|
|
212
|
+
) public view virtual returns (address) {
|
|
213
|
+
return _roleMembers[role].at(index);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @inheritdoc IAccessControlEnumerable
|
|
218
|
+
*/
|
|
219
|
+
function getRoleMemberCount(
|
|
220
|
+
bytes32 role
|
|
221
|
+
) public view virtual returns (uint256) {
|
|
222
|
+
return _roleMembers[role].length();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// --- IAccessControlExtended
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @inheritdoc IAccessControlExtended
|
|
229
|
+
*/
|
|
230
|
+
function rolesOf(
|
|
231
|
+
address account
|
|
232
|
+
) public view virtual returns (bytes32[] memory) {
|
|
233
|
+
return _addressRoles[account].values();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @notice Returns all members that hold `role`.
|
|
238
|
+
*
|
|
239
|
+
* @dev Convenience function that returns the full membership array in a single call.
|
|
240
|
+
* Equivalent to calling {getRoleMember} for each index from `0` to `getRoleMemberCount(role) - 1`.
|
|
241
|
+
*
|
|
242
|
+
* @param role The role identifier to query members for.
|
|
243
|
+
* @return An array of addresses that currently hold the specified role.
|
|
244
|
+
*
|
|
245
|
+
* @custom:warning This function copies the entire role members set from storage into memory.
|
|
246
|
+
* This is designed to mostly be used by view accessors that are queried without any gas fees.
|
|
247
|
+
* For roles with a large number of members, this may consume a significant amount of gas. If calling this function on-chain, consider calling `{getRoleMember}` repeatedly, using `getRoleMemberCount` to know as max index.
|
|
248
|
+
* This function is primarily intended for off-chain usage.
|
|
249
|
+
*/
|
|
250
|
+
function getRoleMembers(
|
|
251
|
+
bytes32 role
|
|
252
|
+
) public view virtual returns (address[] memory) {
|
|
253
|
+
return _roleMembers[role].values();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// --- Internal functions
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* @dev Grants `role` to `account`. No-op if the account already holds the role
|
|
260
|
+
* (matching OZ behavior). Updates both forward and reverse lookups.
|
|
261
|
+
*
|
|
262
|
+
* @custom:events Emits {RoleGranted} if the role was newly granted.
|
|
263
|
+
*/
|
|
264
|
+
function _grantRole(bytes32 role, address account) internal virtual {
|
|
265
|
+
bool added = _roleMembers[role].add(account);
|
|
266
|
+
|
|
267
|
+
if (added) {
|
|
268
|
+
_addressRoles[account].add(role);
|
|
269
|
+
emit RoleGranted({
|
|
270
|
+
role: role,
|
|
271
|
+
account: account,
|
|
272
|
+
sender: msg.sender
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @dev Revokes `role` from `account`. No-op if the account does not hold the role.
|
|
279
|
+
*
|
|
280
|
+
* @custom:events Emits {RoleRevoked} if the role was revoked.
|
|
281
|
+
*/
|
|
282
|
+
function _revokeRole(bytes32 role, address account) internal virtual {
|
|
283
|
+
bool removed = _roleMembers[role].remove(account);
|
|
284
|
+
|
|
285
|
+
if (removed) {
|
|
286
|
+
_addressRoles[account].remove(role);
|
|
287
|
+
emit RoleRevoked({
|
|
288
|
+
role: role,
|
|
289
|
+
account: account,
|
|
290
|
+
sender: msg.sender
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* @dev Checks that `msg.sender` has `role`. Reverts with
|
|
297
|
+
* {AccessControlUnauthorizedAccount} if the check fails.
|
|
298
|
+
*
|
|
299
|
+
* @custom:warning Overriding this function changes the behavior of the {onlyRole} modifier.
|
|
300
|
+
*/
|
|
301
|
+
function _checkRole(bytes32 role) internal view virtual {
|
|
302
|
+
_checkRole(role, msg.sender);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @dev Checks that `account` has `role`.
|
|
307
|
+
*
|
|
308
|
+
* Reverts with {AccessControlUnauthorizedAccount} if the account does not
|
|
309
|
+
* explicitly hold the role.
|
|
310
|
+
*/
|
|
311
|
+
function _checkRole(bytes32 role, address account) internal view virtual {
|
|
312
|
+
require(
|
|
313
|
+
_hasRole(role, account),
|
|
314
|
+
AccessControlUnauthorizedAccount(account, role)
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function _hasRole(
|
|
319
|
+
bytes32 role,
|
|
320
|
+
address account
|
|
321
|
+
) internal view virtual returns (bool) {
|
|
322
|
+
return _roleMembers[role].contains(account);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
|
|
326
|
+
require(
|
|
327
|
+
role != DEFAULT_ADMIN_ROLE,
|
|
328
|
+
AccessControlCannotSetAdminForDefaultAdminRole()
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
bytes32 previousAdminRole = getRoleAdmin(role);
|
|
332
|
+
_roleAdmins[role] = adminRole;
|
|
333
|
+
|
|
334
|
+
emit RoleAdminChanged({
|
|
335
|
+
role: role,
|
|
336
|
+
previousAdminRole: previousAdminRole,
|
|
337
|
+
newAdminRole: adminRole
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// --- Ownership sync
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* @dev Overrides `_transferOwnership` to automatically transfer ALL roles held by
|
|
345
|
+
* the old owner to the new owner. This includes `DEFAULT_ADMIN_ROLE` and any other
|
|
346
|
+
* custom roles the old owner was assigned.
|
|
347
|
+
*
|
|
348
|
+
* For each role held by the old owner:
|
|
349
|
+
* 1. The role is revoked from the old owner.
|
|
350
|
+
* 2. The role is granted to the new owner (if not already held).
|
|
351
|
+
*
|
|
352
|
+
* @custom:info When renouncing ownership, roles are only removed from the old owner. Roles are not passed to `address(0)` (being the `newOwner` in the case of renounce ownership).
|
|
353
|
+
*
|
|
354
|
+
* @custom:warning
|
|
355
|
+
* - Gas cost scales linearly with the number of roles the old owner holds.
|
|
356
|
+
* - If the old owner holds a large number of roles, the transaction may approach or exceed
|
|
357
|
+
* the block gas limit and fail. Avoid assigning too many roles to the owner to ensure
|
|
358
|
+
* ownership transfers remain callable.
|
|
359
|
+
*/
|
|
360
|
+
function _transferOwnership(address newOwner) internal virtual override {
|
|
361
|
+
address oldOwner = owner();
|
|
362
|
+
Ownable._transferOwnership(newOwner);
|
|
363
|
+
|
|
364
|
+
// Snapshot the old owner's roles before mutating storage (values() returns a memory copy)
|
|
365
|
+
bytes32[] memory oldOwnerRoles = _addressRoles[oldOwner].values();
|
|
366
|
+
|
|
367
|
+
for (uint256 ii = 0; ii < oldOwnerRoles.length; ++ii) {
|
|
368
|
+
bytes32 role = oldOwnerRoles[ii];
|
|
369
|
+
|
|
370
|
+
_revokeRole(role, oldOwner);
|
|
371
|
+
|
|
372
|
+
// exclude case when renouncing ownership
|
|
373
|
+
if (newOwner != address(0)) {
|
|
374
|
+
_grantRole(role, newOwner);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
|
+
|
|
4
|
+
/// @dev ERC-165 interface ID for IAccessControl from OpenZeppelin.
|
|
5
|
+
bytes4 constant _INTERFACEID_ACCESSCONTROL = 0x7965db0b;
|
|
6
|
+
|
|
7
|
+
/// @dev ERC-165 interface ID for IAccessControlEnumerable from OpenZeppelin.
|
|
8
|
+
bytes4 constant _INTERFACEID_ACCESSCONTROLENUMERABLE = 0x5a05180f;
|
|
9
|
+
|
|
10
|
+
/// @dev ERC-165 interface ID for IAccessControlExtended.
|
|
11
|
+
/// Computed as XOR of selectors:
|
|
12
|
+
/// getRoleMembers(bytes32) ^ rolesOf(address) ^ setRoleAdmin(bytes32,bytes32)
|
|
13
|
+
bytes4 constant _INTERFACEID_ACCESSCONTROLEXTENDED = 0x90832245;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @dev Reverts when an address attempts a role-gated action and is missing the `neededRole`.
|
|
6
|
+
*
|
|
7
|
+
* @param account The address that attempted the action.
|
|
8
|
+
* @param neededRole The role that was required.
|
|
9
|
+
*/
|
|
10
|
+
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @dev Reverts error when the caller of the `renounceRole` function is not the expected one.
|
|
14
|
+
* NOTE: Do not confuse with {AccessControlUnauthorizedAccount}.
|
|
15
|
+
*/
|
|
16
|
+
error AccessControlBadConfirmation();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @dev Reverts when trying to change the admin of the `DEFAULT_ADMIN_ROLE`.
|
|
20
|
+
* The `DEFAULT_ADMIN_ROLE` is its own admin, meaning only accounts with the `DEFAULT_ADMIN_ROLE` can grant or revoke it.
|
|
21
|
+
* This hierarchy cannot be changed.
|
|
22
|
+
*/
|
|
23
|
+
error AccessControlCannotSetAdminForDefaultAdminRole();
|