Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- AccessControlRegistry
- Optimization enabled
- true
- Compiler version
- v0.8.9+commit.e5eed63a
- Optimization runs
- 1000
- Verified at
- 2022-04-19T15:19:35.038633Z
contracts/access-control-registry/AccessControlRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
import "@openzeppelin/contracts/utils/Multicall.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./RoleDeriver.sol";
import "./interfaces/IAccessControlRegistry.sol";
/// @title Contract that allows users to manage independent, tree-shaped access
/// control tables
/// @notice Multiple contracts can refer to this contract to check if their
/// users have granted accounts specific roles. Therefore, it aims to keep all
/// access control roles of its users in this single contract.
/// @dev Each user is called a "manager", and is the only member of their root
/// role. Starting from this root role, they can create an arbitrary tree of
/// roles and grant these to accounts. Each role has a description, and roles
/// adminned by the same role cannot have the same description.
contract AccessControlRegistry is
Multicall,
AccessControl,
RoleDeriver,
IAccessControlRegistry
{
/// @notice Initializes the manager by initializing its root role and
/// granting it to them
/// @dev Anyone can initialize a manager. An uninitialized manager
/// attempting to initialize a role will be initialized automatically.
/// Once a manager is initialized, subsequent initializations have no
/// effect.
/// @param manager Manager address to be initialized
function initializeManager(address manager) public override {
require(manager != address(0), "Manager address zero");
bytes32 rootRole = deriveRootRole(manager);
if (!hasRole(rootRole, manager)) {
_grantRole(rootRole, manager);
emit InitializedManager(rootRole, manager);
}
}
/// @notice Called by the account to renounce the role
/// @dev Overriden to disallow managers to renounce their root roles.
/// `role` and `account` are not validated because
/// `AccessControl.renounceRole` will revert if either of them is zero.
/// @param role Role to be renounced
/// @param account Account to renounce the role
function renounceRole(bytes32 role, address account)
public
override(AccessControl, IAccessControl)
{
require(
role != deriveRootRole(account),
"role is root role of account"
);
AccessControl.renounceRole(role, account);
}
/// @notice Initializes a role by setting its admin role and grants it to
/// the sender
/// @dev If the sender should not have the initialized role, they should
/// explicitly renounce it after initializing it.
/// Once a role is initialized, subsequent initializations have no effect
/// other than granting the role to the sender.
/// The sender must be a member of `adminRole`. `adminRole` value is not
/// validated because the sender cannot have the `bytes32(0)` role.
/// If the sender is an uninitialized manager that is initializing a role
/// directly under their root role, manager initialization will happen
/// automatically, which will grant the sender `adminRole` and allow them
/// to initialize the role.
/// @param adminRole Admin role to be assigned to the initialized role
/// @param description Human-readable description of the initialized role
/// @return role Initialized role
function initializeRoleAndGrantToSender(
bytes32 adminRole,
string calldata description
) external override returns (bytes32 role) {
require(bytes(description).length > 0, "Role description empty");
role = deriveRole(adminRole, description);
// AccessControl roles have `DEFAULT_ADMIN_ROLE` (i.e., `bytes32(0)`)
// as their `adminRole` by default. No account in AccessControlRegistry
// can possibly have that role, which means all initialized roles will
// have non-default admin roles, and vice versa.
if (getRoleAdmin(role) == DEFAULT_ADMIN_ROLE) {
if (adminRole == deriveRootRole(_msgSender())) {
initializeManager(_msgSender());
}
_setRoleAdmin(role, adminRole);
emit InitializedRole(role, adminRole, description, _msgSender());
}
grantRole(role, _msgSender());
}
/// @notice Derives the root role of the manager
/// @param manager Manager address
/// @return rootRole Root role
function deriveRootRole(address manager)
public
pure
override
returns (bytes32 rootRole)
{
rootRole = _deriveRootRole(manager);
}
/// @notice Derives the role using its admin role and description
/// @dev This implies that roles adminned by the same role cannot have the
/// same description
/// @param adminRole Admin role
/// @param description Human-readable description of the role
/// @return role Role
function deriveRole(bytes32 adminRole, string calldata description)
public
pure
override
returns (bytes32 role)
{
role = _deriveRole(adminRole, description);
}
}
@openzeppelin/contracts/access/AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
@openzeppelin/contracts/access/IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
@openzeppelin/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
@openzeppelin/contracts/utils/Multicall.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Multicall.sol)
pragma solidity ^0.8.0;
import "./Address.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
abstract contract Multicall {
/**
* @dev Receives and executes a batch of function calls on this contract.
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), data[i]);
}
return results;
}
}
@openzeppelin/contracts/utils/Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
@openzeppelin/contracts/utils/introspection/ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
@openzeppelin/contracts/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
contracts/access-control-registry/RoleDeriver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Contract to be inherited by contracts that will derive
/// AccessControlRegistry roles
/// @notice If a contract interfaces with AccessControlRegistry and needs to
/// derive roles, it should inherit this contract instead of re-implementing
/// the logic
contract RoleDeriver {
/// @notice Derives the root role of the manager
/// @param manager Manager address
/// @return rootRole Root role
function _deriveRootRole(address manager)
internal
pure
returns (bytes32 rootRole)
{
rootRole = keccak256(abi.encodePacked(manager));
}
/// @notice Derives the role using its admin role and description
/// @dev This implies that roles adminned by the same role cannot have the
/// same description
/// @param adminRole Admin role
/// @param description Human-readable description of the role
/// @return role Role
function _deriveRole(bytes32 adminRole, string memory description)
internal
pure
returns (bytes32 role)
{
role = _deriveRole(adminRole, keccak256(abi.encodePacked(description)));
}
/// @notice Derives the role using its admin role and description hash
/// @dev This implies that roles adminned by the same role cannot have the
/// same description
/// @param adminRole Admin role
/// @param descriptionHash Hash of the human-readable description of the
/// role
/// @return role Role
function _deriveRole(bytes32 adminRole, bytes32 descriptionHash)
internal
pure
returns (bytes32 role)
{
role = keccak256(abi.encodePacked(adminRole, descriptionHash));
}
}
contracts/access-control-registry/interfaces/IAccessControlRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/IAccessControl.sol";
interface IAccessControlRegistry is IAccessControl {
event InitializedManager(bytes32 indexed rootRole, address indexed manager);
event InitializedRole(
bytes32 indexed role,
bytes32 indexed adminRole,
string description,
address sender
);
function initializeManager(address manager) external;
function initializeRoleAndGrantToSender(
bytes32 adminRole,
string calldata description
) external returns (bytes32 role);
function deriveRootRole(address manager)
external
pure
returns (bytes32 rootRole);
function deriveRole(bytes32 adminRole, string calldata description)
external
pure
returns (bytes32 role);
}
Contract ABI
[{"type":"event","name":"InitializedManager","inputs":[{"type":"bytes32","name":"rootRole","internalType":"bytes32","indexed":true},{"type":"address","name":"manager","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"InitializedRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"adminRole","internalType":"bytes32","indexed":true},{"type":"string","name":"description","internalType":"string","indexed":false},{"type":"address","name":"sender","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"previousAdminRole","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"newAdminRole","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DEFAULT_ADMIN_ROLE","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}],"name":"deriveRole","inputs":[{"type":"bytes32","name":"adminRole","internalType":"bytes32"},{"type":"string","name":"description","internalType":"string"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"rootRole","internalType":"bytes32"}],"name":"deriveRootRole","inputs":[{"type":"address","name":"manager","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getRoleAdmin","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initializeManager","inputs":[{"type":"address","name":"manager","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}],"name":"initializeRoleAndGrantToSender","inputs":[{"type":"bytes32","name":"adminRole","internalType":"bytes32"},{"type":"string","name":"description","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes[]","name":"results","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"supportsInterface","inputs":[{"type":"bytes4","name":"interfaceId","internalType":"bytes4"}]}]
Contract Creation Code
0x608060405234801561001057600080fd5b50611145806100206000396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c8063805d43fc11610081578063ac9650d81161005b578063ac9650d8146101d2578063b530b5e0146101f2578063d547741f1461020557600080fd5b8063805d43fc1461018057806391d1485414610193578063a217fddf146101ca57600080fd5b806336568abe116100b257806336568abe1461014757806373e983621461015a5780637f7120fe1461016d57600080fd5b806301ffc9a7146100d9578063248a9ca3146101015780632f2ff15d14610132575b600080fd5b6100ec6100e7366004610c90565b610218565b60405190151581526020015b60405180910390f35b61012461010f366004610cd2565b60009081526020819052604090206001015490565b6040519081526020016100f8565b610145610140366004610d07565b6102b1565b005b610145610155366004610d07565b6102dc565b610124610168366004610d33565b610347565b61014561017b366004610daf565b61042e565b61012461018e366004610daf565b6104fe565b6100ec6101a1366004610d07565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610124600081565b6101e56101e0366004610dca565b61053e565b6040516100f89190610e9b565b610124610200366004610d33565b610633565b610145610213366004610d07565b61067d565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806102ab57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b6000828152602081905260409020600101546102cd81336106a3565b6102d78383610721565b505050565b6102e5816104fe565b8214156103395760405162461bcd60e51b815260206004820152601c60248201527f726f6c6520697320726f6f7420726f6c65206f66206163636f756e740000000060448201526064015b60405180910390fd5b61034382826107bf565b5050565b6000816103965760405162461bcd60e51b815260206004820152601660248201527f526f6c65206465736372697074696f6e20656d707479000000000000000000006044820152606401610330565b6103a1848484610633565b600081815260208190526040812060010154919250141561041d576103c5336104fe565b8414156103d5576103d53361042e565b6103df8185610847565b83817f532ead3ec09896bef1351791fbaad86ac03f3204090a8e7f173f41414b1fdac085853360405161041493929190610efd565b60405180910390a35b61042781336102b1565b9392505050565b6001600160a01b0381166104845760405162461bcd60e51b815260206004820152601460248201527f4d616e616765722061646472657373207a65726f0000000000000000000000006044820152606401610330565b600061048f826104fe565b6000818152602081815260408083206001600160a01b038716845290915290205490915060ff16610343576104c48183610721565b6040516001600160a01b0383169082907f888b171f3b02386c0e4d8c85108dcb8d0ecdad2f274ddc7ce3914282538bdd8890600090a35050565b60408051606083901b6bffffffffffffffffffffffff191660208083019190915282516014818403018152603490920190925280519101206000906102ab565b60608167ffffffffffffffff81111561055957610559610f3f565b60405190808252806020026020018201604052801561058c57816020015b60608152602001906001900390816105775790505b50905060005b8281101561062c576105fc308585848181106105b0576105b0610f55565b90506020028101906105c29190610f6b565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061089292505050565b82828151811061060e5761060e610f55565b6020026020010181905250808061062490610fcf565b915050610592565b5092915050565b60006106758484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506108b792505050565b949350505050565b60008281526020819052604090206001015461069981336106a3565b6102d7838361090c565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610343576106df816001600160a01b0316601461098b565b6106ea83602061098b565b6040516020016106fb929190610fea565b60408051601f198184030181529082905262461bcd60e51b82526103309160040161106b565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610343576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561077b3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6001600160a01b038116331461083d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152608401610330565b610343828261090c565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b606061042783836040518060600160405280602781526020016110e960279139610b6c565b600061042783836040516020016108ce919061107e565b60408051601f198184030181528282528051602091820120838201949094528282019390935280518083038201815260609092019052805191012090565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615610343576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6060600061099a83600261109a565b6109a59060026110b9565b67ffffffffffffffff8111156109bd576109bd610f3f565b6040519080825280601f01601f1916602001820160405280156109e7576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110610a1e57610a1e610f55565b60200101906001600160f81b031916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110610a6957610a69610f55565b60200101906001600160f81b031916908160001a9053506000610a8d84600261109a565b610a989060016110b9565b90505b6001811115610b1d577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110610ad957610ad9610f55565b1a60f81b828281518110610aef57610aef610f55565b60200101906001600160f81b031916908160001a90535060049490941c93610b16816110d1565b9050610a9b565b5083156104275760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610330565b6060833b610be25760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610330565b600080856001600160a01b031685604051610bfd919061107e565b600060405180830381855af49150503d8060008114610c38576040519150601f19603f3d011682016040523d82523d6000602084013e610c3d565b606091505b5091509150610c4d828286610c57565b9695505050505050565b60608315610c66575081610427565b825115610c765782518084602001fd5b8160405162461bcd60e51b8152600401610330919061106b565b600060208284031215610ca257600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461042757600080fd5b600060208284031215610ce457600080fd5b5035919050565b80356001600160a01b0381168114610d0257600080fd5b919050565b60008060408385031215610d1a57600080fd5b82359150610d2a60208401610ceb565b90509250929050565b600080600060408486031215610d4857600080fd5b83359250602084013567ffffffffffffffff80821115610d6757600080fd5b818601915086601f830112610d7b57600080fd5b813581811115610d8a57600080fd5b876020828501011115610d9c57600080fd5b6020830194508093505050509250925092565b600060208284031215610dc157600080fd5b61042782610ceb565b60008060208385031215610ddd57600080fd5b823567ffffffffffffffff80821115610df557600080fd5b818501915085601f830112610e0957600080fd5b813581811115610e1857600080fd5b8660208260051b8501011115610e2d57600080fd5b60209290920196919550909350505050565b60005b83811015610e5a578181015183820152602001610e42565b83811115610e69576000848401525b50505050565b60008151808452610e87816020860160208601610e3f565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015610ef057603f19888603018452610ede858351610e6f565b94509285019290850190600101610ec2565b5092979650505050505050565b604081528260408201528284606083013760006060848301015260006060601f19601f86011683010190506001600160a01b0383166020830152949350505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112610f8257600080fd5b83018035915067ffffffffffffffff821115610f9d57600080fd5b602001915036819003821315610fb257600080fd5b9250929050565b634e487b7160e01b600052601160045260246000fd5b6000600019821415610fe357610fe3610fb9565b5060010190565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351611022816017850160208801610e3f565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000601791840191820152835161105f816028840160208801610e3f565b01602801949350505050565b6020815260006104276020830184610e6f565b60008251611090818460208701610e3f565b9190910192915050565b60008160001904831182151516156110b4576110b4610fb9565b500290565b600082198211156110cc576110cc610fb9565b500190565b6000816110e0576110e0610fb9565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212207cc002eec550ca333e5647aeee1bec01baf44cbf2540ae519fcdad161591c81864736f6c63430008090033
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106100d45760003560e01c8063805d43fc11610081578063ac9650d81161005b578063ac9650d8146101d2578063b530b5e0146101f2578063d547741f1461020557600080fd5b8063805d43fc1461018057806391d1485414610193578063a217fddf146101ca57600080fd5b806336568abe116100b257806336568abe1461014757806373e983621461015a5780637f7120fe1461016d57600080fd5b806301ffc9a7146100d9578063248a9ca3146101015780632f2ff15d14610132575b600080fd5b6100ec6100e7366004610c90565b610218565b60405190151581526020015b60405180910390f35b61012461010f366004610cd2565b60009081526020819052604090206001015490565b6040519081526020016100f8565b610145610140366004610d07565b6102b1565b005b610145610155366004610d07565b6102dc565b610124610168366004610d33565b610347565b61014561017b366004610daf565b61042e565b61012461018e366004610daf565b6104fe565b6100ec6101a1366004610d07565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610124600081565b6101e56101e0366004610dca565b61053e565b6040516100f89190610e9b565b610124610200366004610d33565b610633565b610145610213366004610d07565b61067d565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806102ab57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b6000828152602081905260409020600101546102cd81336106a3565b6102d78383610721565b505050565b6102e5816104fe565b8214156103395760405162461bcd60e51b815260206004820152601c60248201527f726f6c6520697320726f6f7420726f6c65206f66206163636f756e740000000060448201526064015b60405180910390fd5b61034382826107bf565b5050565b6000816103965760405162461bcd60e51b815260206004820152601660248201527f526f6c65206465736372697074696f6e20656d707479000000000000000000006044820152606401610330565b6103a1848484610633565b600081815260208190526040812060010154919250141561041d576103c5336104fe565b8414156103d5576103d53361042e565b6103df8185610847565b83817f532ead3ec09896bef1351791fbaad86ac03f3204090a8e7f173f41414b1fdac085853360405161041493929190610efd565b60405180910390a35b61042781336102b1565b9392505050565b6001600160a01b0381166104845760405162461bcd60e51b815260206004820152601460248201527f4d616e616765722061646472657373207a65726f0000000000000000000000006044820152606401610330565b600061048f826104fe565b6000818152602081815260408083206001600160a01b038716845290915290205490915060ff16610343576104c48183610721565b6040516001600160a01b0383169082907f888b171f3b02386c0e4d8c85108dcb8d0ecdad2f274ddc7ce3914282538bdd8890600090a35050565b60408051606083901b6bffffffffffffffffffffffff191660208083019190915282516014818403018152603490920190925280519101206000906102ab565b60608167ffffffffffffffff81111561055957610559610f3f565b60405190808252806020026020018201604052801561058c57816020015b60608152602001906001900390816105775790505b50905060005b8281101561062c576105fc308585848181106105b0576105b0610f55565b90506020028101906105c29190610f6b565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061089292505050565b82828151811061060e5761060e610f55565b6020026020010181905250808061062490610fcf565b915050610592565b5092915050565b60006106758484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506108b792505050565b949350505050565b60008281526020819052604090206001015461069981336106a3565b6102d7838361090c565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610343576106df816001600160a01b0316601461098b565b6106ea83602061098b565b6040516020016106fb929190610fea565b60408051601f198184030181529082905262461bcd60e51b82526103309160040161106b565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610343576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561077b3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6001600160a01b038116331461083d5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152608401610330565b610343828261090c565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b606061042783836040518060600160405280602781526020016110e960279139610b6c565b600061042783836040516020016108ce919061107e565b60408051601f198184030181528282528051602091820120838201949094528282019390935280518083038201815260609092019052805191012090565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615610343576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6060600061099a83600261109a565b6109a59060026110b9565b67ffffffffffffffff8111156109bd576109bd610f3f565b6040519080825280601f01601f1916602001820160405280156109e7576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110610a1e57610a1e610f55565b60200101906001600160f81b031916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110610a6957610a69610f55565b60200101906001600160f81b031916908160001a9053506000610a8d84600261109a565b610a989060016110b9565b90505b6001811115610b1d577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110610ad957610ad9610f55565b1a60f81b828281518110610aef57610aef610f55565b60200101906001600160f81b031916908160001a90535060049490941c93610b16816110d1565b9050610a9b565b5083156104275760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610330565b6060833b610be25760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610330565b600080856001600160a01b031685604051610bfd919061107e565b600060405180830381855af49150503d8060008114610c38576040519150601f19603f3d011682016040523d82523d6000602084013e610c3d565b606091505b5091509150610c4d828286610c57565b9695505050505050565b60608315610c66575081610427565b825115610c765782518084602001fd5b8160405162461bcd60e51b8152600401610330919061106b565b600060208284031215610ca257600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461042757600080fd5b600060208284031215610ce457600080fd5b5035919050565b80356001600160a01b0381168114610d0257600080fd5b919050565b60008060408385031215610d1a57600080fd5b82359150610d2a60208401610ceb565b90509250929050565b600080600060408486031215610d4857600080fd5b83359250602084013567ffffffffffffffff80821115610d6757600080fd5b818601915086601f830112610d7b57600080fd5b813581811115610d8a57600080fd5b876020828501011115610d9c57600080fd5b6020830194508093505050509250925092565b600060208284031215610dc157600080fd5b61042782610ceb565b60008060208385031215610ddd57600080fd5b823567ffffffffffffffff80821115610df557600080fd5b818501915085601f830112610e0957600080fd5b813581811115610e1857600080fd5b8660208260051b8501011115610e2d57600080fd5b60209290920196919550909350505050565b60005b83811015610e5a578181015183820152602001610e42565b83811115610e69576000848401525b50505050565b60008151808452610e87816020860160208601610e3f565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015610ef057603f19888603018452610ede858351610e6f565b94509285019290850190600101610ec2565b5092979650505050505050565b604081528260408201528284606083013760006060848301015260006060601f19601f86011683010190506001600160a01b0383166020830152949350505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112610f8257600080fd5b83018035915067ffffffffffffffff821115610f9d57600080fd5b602001915036819003821315610fb257600080fd5b9250929050565b634e487b7160e01b600052601160045260246000fd5b6000600019821415610fe357610fe3610fb9565b5060010190565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351611022816017850160208801610e3f565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000601791840191820152835161105f816028840160208801610e3f565b01602801949350505050565b6020815260006104276020830184610e6f565b60008251611090818460208701610e3f565b9190910192915050565b60008160001904831182151516156110b4576110b4610fb9565b500290565b600082198211156110cc576110cc610fb9565b500190565b6000816110e0576110e0610fb9565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212207cc002eec550ca333e5647aeee1bec01baf44cbf2540ae519fcdad161591c81864736f6c63430008090033