// File: StdReferenceTick.sol



// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.13;

import {StdReferenceBase} from "StdReferenceBase.sol";
import {AccessControl} from "AccessControl.sol";

contract StdReferenceTick is AccessControl, StdReferenceBase {
    bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
    bytes32 public constant LISTER_ROLE = keccak256("LISTER_ROLE");
    bytes32 public constant DELISTER_ROLE = keccak256("DELISTER_ROLE");
    bytes32 private constant USD = keccak256(bytes("USD"));
    uint256 public constant MID_TICK = 262144;

    struct Price {
        uint256 tick;
        string symbol;
    }

    uint256 public totalSymbolsCount = 0;

    // storage
    // 31|3|(timeOffset(18)+tick(19))*6|
    mapping(uint256 => uint256) public refs;
    mapping(string => uint256) public symbolsToIDs;
    mapping(uint256 => string) public idsToSymbols;

    constructor(address admin) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(RELAYER_ROLE, admin);
        _grantRole(LISTER_ROLE, admin);
        _grantRole(DELISTER_ROLE, admin);
    }

    function _extractSlotTime(uint256 val) private pure returns (uint256 t) {
        unchecked {
            t = (val >> 225) & ((1 << 31) - 1);
        }
    }

    function _extractSize(uint256 val) private pure returns (uint256 s) {
        unchecked {
            s = (val >> 222) & ((1 << 3) - 1);
        }
    }

    function _extractTick(uint256 val, uint256 shiftLen) private pure returns (uint256 tick) {
        unchecked {
            tick = (val >> shiftLen) & ((1 << 19) - 1);
        }
    }

    function _extractTimeOffset(uint256 val, uint256 shiftLen) private pure returns (uint256 offset) {
        unchecked {
            offset = (val >> shiftLen) & ((1 << 18) - 1);
        }
    }

    function _setTime(uint256 val, uint256 time) private pure returns (uint256 newVal) {
        unchecked {
            newVal = (val & (type(uint256).max >> 31)) | (time << 225);
        }
    }

    function _setSize(uint256 val, uint256 size) private pure returns (uint256 newVal) {
        unchecked {
            newVal = (val & ((type(uint256).max << (37 * (6 - size))) - ((((1 << 3) - 1)) << 222))) | (size << 222);
        }
    }

    function _setTimeOffset(uint256 val, uint256 timeOffset, uint256 shiftLen) private pure returns (uint256 newVal) {
        unchecked {
            newVal = ((val & ~(uint256((1 << 18) - 1) << (shiftLen + 19))) | (timeOffset << (shiftLen + 19)));
        }
    }

    function _setTicksAndTimeOffset(uint256 val, uint256 timeOffset, uint256 tick, uint256 shiftLen) private pure returns (uint256 newVal) {
        unchecked {
            newVal = (val & (~(uint256((1 << 37) - 1) << shiftLen))) | (((timeOffset << 19) | (tick & ((1 << 19) - 1))) << shiftLen);
        }
    }

    function _getTickAndTime(uint256 slot, uint8 idx) private view returns (uint256 tick, uint256 lastUpdated) {
        unchecked {
            uint256 sVal = refs[slot];
            uint256 idx_x_37 = idx * 37;
            return (_extractTick(sVal, 185 - idx_x_37), _extractTimeOffset(sVal, 204 - idx_x_37) + _extractSlotTime(sVal));
        }
    }

    function getSlotAndIndex(string memory symbol) public view returns (uint256 slot, uint8 idx) {
        unchecked {
            uint256 id = symbolsToIDs[symbol];
            require(id != 0, "getSlotAndIndex: FAIL_SYMBOL_NOT_AVAILABLE");
            return ((id - 1) / 6, uint8((id - 1) % 6));
        }
    }

    function getTickAndTime(string memory symbol) public view returns (uint256 tick, uint256 lastUpdated) {
        unchecked {
            if (keccak256(bytes(symbol)) == USD) {
                (tick, lastUpdated) = (MID_TICK, block.timestamp);
            } else {
                (uint256 slot, uint8 idx) = getSlotAndIndex(symbol);
                (tick, lastUpdated) = _getTickAndTime(slot, idx);
                require(tick != 0, "getTickAndTime: FAIL_TICK_0_IS_AN_EMPTY_PRICE");
            }
        }
    }

    function getReferenceData(string memory _base, string memory _quote) public view override returns (ReferenceData memory r) {
        uint256 baseTick;
        uint256 quoteTick;
        (baseTick, r.lastUpdatedBase) = getTickAndTime(_base);
        (quoteTick, r.lastUpdatedQuote) = getTickAndTime(_quote);
        require(baseTick + MID_TICK > quoteTick, "getReferenceData: FAIL_PRICE_RATIO_TOO_LOW");
        require(baseTick < MID_TICK + quoteTick, "getReferenceData: FAIL_PRICE_RATIO_TOO_HIGH");
        r.rate = _getPriceFromTick((baseTick + MID_TICK) - quoteTick);
    }

    function _getPriceFromTick(uint256 x) private pure returns (uint256 y) {
        unchecked {
            require(x != 0, "_getPriceFromTick: FAIL_TICK_0_IS_AN_EMPTY_PRICE");
            require(x < (1 << 19), "_getPriceFromTick: FAIL_TICK_OUT_OF_RANGE");
            y = 649037107316853453566312041152512;
            if (x < MID_TICK) {
                x = MID_TICK - x;
                if (x & 0x01 != 0) y = (y * 649102011027585138911668672356627) >> 109;
                if (x & 0x02 != 0) y = (y * 649166921228687897425559839223862) >> 109;
                if (x & 0x04 != 0) y = (y * 649296761104602847291923925447306) >> 109;
                if (x & 0x08 != 0) y = (y * 649556518769447606681106054382372) >> 109;
                if (x & 0x10 != 0) y = (y * 650076345896668132522271100656030) >> 109;
                if (x & 0x20 != 0) y = (y * 651117248505878973533694452870408) >> 109;
                if (x & 0x40 != 0) y = (y * 653204056474534657407624669811404) >> 109;
                if (x & 0x80 != 0) y = (y * 657397758286396885483233885325217) >> 109;
                if (x & 0x0100 != 0) y = (y * 665866108005128170549362417755489) >> 109;
                if (x & 0x0200 != 0) y = (y * 683131470899774684431604377857106) >> 109;
                if (x & 0x0400 != 0) y = (y * 719016834742958293196733842540130) >> 109;
                if (x & 0x0800 != 0) y = (y * 796541835305874991615834691778664) >> 109;
                if (x & 0x1000 != 0) y = (y * 977569522974447437629335387266319) >> 109;
                if (x & 0x2000 != 0) y = (y * 1472399900522103311842374358851872) >> 109;
                if (x & 0x4000 != 0) y = (y * 3340273526146976564083509455290620) >> 109;
                if (x & 0x8000 != 0) y = (y * 17190738562859105750521122099339319) >> 109;
                if (x & 0x010000 != 0) y = (y * 455322953040804340936374685561109626) >> 109;
                if (x & 0x020000 != 0) y = (y * 319425483117388922324853186559947171877) >> 109;
                y = 649037107316853453566312041152512000000000000000000 / y;
            } else {
                x = x - MID_TICK;
                if (x & 0x01 != 0) y = (y * 649102011027585138911668672356627) >> 109;
                if (x & 0x02 != 0) y = (y * 649166921228687897425559839223862) >> 109;
                if (x & 0x04 != 0) y = (y * 649296761104602847291923925447306) >> 109;
                if (x & 0x08 != 0) y = (y * 649556518769447606681106054382372) >> 109;
                if (x & 0x10 != 0) y = (y * 650076345896668132522271100656030) >> 109;
                if (x & 0x20 != 0) y = (y * 651117248505878973533694452870408) >> 109;
                if (x & 0x40 != 0) y = (y * 653204056474534657407624669811404) >> 109;
                if (x & 0x80 != 0) y = (y * 657397758286396885483233885325217) >> 109;
                if (x & 0x0100 != 0) y = (y * 665866108005128170549362417755489) >> 109;
                if (x & 0x0200 != 0) y = (y * 683131470899774684431604377857106) >> 109;
                if (x & 0x0400 != 0) y = (y * 719016834742958293196733842540130) >> 109;
                if (x & 0x0800 != 0) y = (y * 796541835305874991615834691778664) >> 109;
                if (x & 0x1000 != 0) y = (y * 977569522974447437629335387266319) >> 109;
                if (x & 0x2000 != 0) y = (y * 1472399900522103311842374358851872) >> 109;
                if (x & 0x4000 != 0) y = (y * 3340273526146976564083509455290620) >> 109;
                if (x & 0x8000 != 0) y = (y * 17190738562859105750521122099339319) >> 109;
                if (x & 0x010000 != 0) y = (y * 455322953040804340936374685561109626) >> 109;
                if (x & 0x020000 != 0) y = (y * 319425483117388922324853186559947171877) >> 109;
                y = (y * 1e18) / 649037107316853453566312041152512;
            }
        }
    }

    function getPriceFromTick(uint256 x) public pure returns (uint256 y) {
        y = _getPriceFromTick(x);
    }

    /**
     * @dev Grants `RELAYER_ROLE` to `accounts`.
     *
     * If each `account` had not been already granted `RELAYER_ROLE`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``RELAYER_ROLE``'s admin role.
     */
    function grantRelayers(address[] calldata accounts) external onlyRole(getRoleAdmin(RELAYER_ROLE)) {
        for (uint256 idx = 0; idx < accounts.length; idx++) {
            _grantRole(RELAYER_ROLE, accounts[idx]);
        }
    }

    /**
     * @dev Revokes `RELAYER_ROLE` from `accounts`.
     *
     * If each `account` had already granted `RELAYER_ROLE`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``RELAYER_ROLE``'s admin role.
     */
    function revokeRelayers(address[] calldata accounts) external onlyRole(getRoleAdmin(RELAYER_ROLE)) {
        for (uint256 idx = 0; idx < accounts.length; idx++) {
            _revokeRole(RELAYER_ROLE, accounts[idx]);
        }
    }

    function listing(string[] calldata symbols) public onlyRole(LISTER_ROLE) {
        require(symbols.length != 0, "listing: FAIL_SYMBOLS_IS_EMPTY");

        uint256 _totalSymbolsCount = totalSymbolsCount;
        uint256 sid = _totalSymbolsCount / 6;
        uint256 sVal = refs[sid];
        uint256 sSize = _extractSize(sVal);

        for (uint256 i = 0; i < symbols.length; i++) {
            require(keccak256(bytes(symbols[i])) != USD, "listing: FAIL_USD_CANT_BE_SET");
            require(symbolsToIDs[symbols[i]] == 0, "listing: FAIL_SYMBOL_IS_ALREADY_SET");

            uint256 slotID = _totalSymbolsCount / 6;

            _totalSymbolsCount++;
            symbolsToIDs[symbols[i]] = _totalSymbolsCount;
            idsToSymbols[_totalSymbolsCount] = symbols[i];

            if (sid != slotID) {
                refs[sid] = sVal;

                sid = slotID;
                sVal = refs[sid];
                sSize = _extractSize(sVal);
            }

            sSize++;
            sVal = _setSize(sVal, sSize);
        }

        refs[sid] = sVal;
        totalSymbolsCount = _totalSymbolsCount;
    }

    function delisting(string[] calldata symbols) public onlyRole(DELISTER_ROLE) {
        uint256 _totalSymbolsCount = totalSymbolsCount;
        uint256 slotID1;
        uint256 slotID2;
        uint256 sVal1;
        uint256 sVal2;
        uint256 sSize;
        uint256 shiftLen;
        uint256 lastSegment;
        uint256 time;
        string memory lastSymbol;
        for (uint256 i = 0; i < symbols.length; i++) {
            uint256 id = symbolsToIDs[symbols[i]];
            require(id != 0, "delisting: FAIL_SYMBOL_NOT_AVAILABLE");

            lastSymbol = idsToSymbols[_totalSymbolsCount];

            symbolsToIDs[lastSymbol] = id;
            idsToSymbols[id] = lastSymbol;

            slotID1 = (_totalSymbolsCount - 1) / 6;
            slotID2 = (id - 1) / 6;
            sVal1 = refs[slotID1];
            sSize = _extractSize(sVal1);
            lastSegment = (sVal1 >> (37 * (6 - sSize))) & ((1 << 37) - 1);
            shiftLen = 37 * (5 - ((id - 1) % 6));

            if (slotID1 == slotID2) {
                sVal1 = (sVal1 & (type(uint256).max - (((1 << 37) - 1) << shiftLen))) | (lastSegment << shiftLen);
            } else {
                sVal2 = refs[slotID2];

                time = _extractSlotTime(sVal1) + (lastSegment >> 19);
                require(time >= _extractSlotTime(sVal2), "delisting: FAIL_LAST_TIMESTAMP_IS_LESS_THAN_TARGET_TIMESTAMP");
                time -= _extractSlotTime(sVal2);
                require(time < 1 << 18, "delisting: FAIL_DELTA_TIME_EXCEED_3_DAYS");
                lastSegment = (time << 19) | (lastSegment & ((1 << 19) - 1));

                refs[slotID2] = (sVal2 & (type(uint256).max - (((1 << 37) - 1) << shiftLen))) | (lastSegment << shiftLen);
            }

            refs[slotID1] = _setSize(sVal1, sSize - 1);

            delete symbolsToIDs[symbols[i]];
            delete idsToSymbols[_totalSymbolsCount];

            _totalSymbolsCount--;
        }

        totalSymbolsCount = _totalSymbolsCount;
    }

    function relay(uint256 time, uint256 requestID, Price[] calldata ps) external onlyRole(RELAYER_ROLE) {
        unchecked {
            uint256 id;
            uint256 sid = type(uint256).max;
            uint256 nextSID;
            uint256 sTime;
            uint256 sVal;
            uint256 shiftLen;
            for (uint256 i = 0; i < ps.length; i++) {
                id = symbolsToIDs[ps[i].symbol];
                require(id != 0, "relay: FAIL_SYMBOL_NOT_AVAILABLE");

                nextSID = (id - 1) / 6;
                if (sid != nextSID) {
                    if (sVal != 0) refs[sid] = sVal;

                    sVal = refs[nextSID];
                    sid = nextSID;
                    sTime = _extractSlotTime(sVal);
                }

                shiftLen = 204 - (37 * ((id - 1) % 6));
                if (sTime + _extractTimeOffset(sVal, shiftLen) < time) {
                    require(time < sTime + (1 << 18), "relay: FAIL_DELTA_TIME_EXCEED_3_DAYS");
                    sVal = _setTicksAndTimeOffset(sVal, time - sTime, ps[i].tick, shiftLen - 19);
                }
            }

            if (sVal != 0) refs[sid] = sVal;
        }
    }

    function relayRebase(uint256 time, uint256 requestID, Price[] calldata ps) external onlyRole(RELAYER_ROLE) {
        unchecked {
            uint256 id;
            uint256 nextID;
            uint256 sVal;
            uint256 sTime;
            uint256 sSize;
            uint256 shiftLen;
            uint256 timeOffset;
            uint256 i;
            while (i < ps.length) {
                id = symbolsToIDs[ps[i].symbol];
                require(id != 0, "relayRebase: FAIL_SYMBOL_NOT_AVAILABLE");
                require((id - 1) % 6 == 0, "relayRebase: FAIL_INVALID_FIRST_IDX");
                sVal = refs[(id - 1) / 6];
                (sTime, sSize) = (_extractSlotTime(sVal), _extractSize(sVal));
                require(sTime < time, "relayRebase: FAIL_NEW_TIME_<=_CURRENT");
                shiftLen = 204;
                timeOffset = _extractTimeOffset(sVal, shiftLen);
                shiftLen = shiftLen - 19;
                sVal = sTime + timeOffset <= time
                    ? _setTicksAndTimeOffset(sVal, 0, ps[i].tick, shiftLen)
                    : _setTimeOffset(sVal, (sTime + timeOffset) - time, shiftLen);
                require(i + sSize <= ps.length, "relayRebase: FAIL_INCONSISTENT_SIZES");
                for (uint256 j = i + 1; j < i + sSize; j++) {
                    nextID = symbolsToIDs[ps[j].symbol];
                    require(nextID != 0, "relayRebase: FAIL_SYMBOL_NOT_AVAILABLE");
                    require(nextID + i == id + j, "relayRebase: FAIL_INVALID_IDX_ORDER");
                    shiftLen = shiftLen - 18;
                    timeOffset = _extractTimeOffset(sVal, shiftLen);
                    shiftLen = shiftLen - 19;
                    sVal = sTime + timeOffset <= time
                        ? _setTicksAndTimeOffset(sVal, 0, ps[j].tick, shiftLen)
                        : _setTimeOffset(sVal, (sTime + timeOffset) - time, shiftLen);
                }
                refs[(id - 1) / 6] = _setTime(sVal, time);
                i += sSize;
            }
        }
    }
}

// File: StdReferenceBase.sol



// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.13;

import {IStdReference} from "IStdReference.sol";

abstract contract StdReferenceBase is IStdReference {
    function getReferenceData(string memory _base, string memory _quote) public view virtual override returns (ReferenceData memory);

    function getReferenceDataBulk(string[] memory _bases, string[] memory _quotes) public view override returns (ReferenceData[] memory) {
        require(_bases.length == _quotes.length, "BAD_INPUT_LENGTH");
        uint256 len = _bases.length;
        ReferenceData[] memory results = new ReferenceData[](len);
        for (uint256 idx = 0; idx < len; idx++) {
            results[idx] = getReferenceData(_bases[idx], _quotes[idx]);
        }
        return results;
    }
}

// File: IStdReference.sol



// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.13;

interface IStdReference {
    /// A structure returned whenever someone requests for standard reference data.
    struct ReferenceData {
        uint256 rate; // base/quote exchange rate, multiplied by 1e18.
        uint256 lastUpdatedBase; // UNIX epoch of the last time when base price gets updated.
        uint256 lastUpdatedQuote; // UNIX epoch of the last time when quote price gets updated.
    }

    /// Returns the price data for the given base/quote pair. Revert if not available.
    function getReferenceData(string memory _base, string memory _quote) external view returns (ReferenceData memory);

    /// Similar to getReferenceData, but with multiple base/quote pairs at once.
    function getReferenceDataBulk(string[] memory _bases, string[] memory _quotes) external view returns (ReferenceData[] memory);
}

// File: AccessControl.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "IAccessControl.sol";
import "Context.sol";
import "Strings.sol";
import "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 virtual 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 virtual {
        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 virtual 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());
        }
    }
}

// File: 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;
}

// File: 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;
    }
}

// File: 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);
    }
}

// File: 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;
    }
}

// File: 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);
}