// File: contracts/modules/licensing/LicensingFrameworkRepo.sol



// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { Licensing } from "contracts/lib/modules/Licensing.sol";
import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol";
import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol";
import { Errors } from "contracts/lib/Errors.sol";
import { AccessControlled } from "contracts/access-control/AccessControlled.sol";
import { AccessControl } from "contracts/lib/AccessControl.sol";
import { FixedSet } from "contracts/utils/FixedSet.sol";

/// @title Licensing Framework Repo
/// @notice Stores licensing frameworks and their parameters. License Modules
/// can use this repo to fetch the parameters of a framework in order encode and decode
/// them when creating Licenses
contract LicensingFrameworkRepo is AccessControlled, Multicall {
    using FixedSet for FixedSet.ShortStringSet;
    using ShortStrings for *;

    /// @notice Struct for storing the parameters of a licensing framework
    struct FrameworkStorage {
        /// @notice URL to the legal document of the framework
        string textUrl;
        /// @notice The tags of the parameters of the framework.
        FixedSet.ShortStringSet paramTags;
        /// @notice The definitions of the parameters of the framework
        Licensing.ParamDefinition[] paramDefs;
    }

    /// Emits when a new licensing framework is added
    event FrameworkAdded(string frameworkId, string textUrl);

    /// emits when a new parameter is added to a framework
    event ParamDefinitionAdded(string frameworkId, ShortString tag, Licensing.ParamDefinition definition);

    /// frameworkId => FrameworkStorage
    mapping(string => FrameworkStorage) private _frameworks;
    /// Hash of (frameworkId, tag) => ParamDefinition
    mapping(bytes32 => Licensing.ParamDefinition) private _frameworkDefs;

    /// @notice Constructor for the repo
    /// @param accessControl_ the address of the access control singleton contract
    constructor(address accessControl_) AccessControlled(accessControl_) {}

    /// @notice Adds a new licensing framework to the repo
    /// @dev this is an admin only function, and can only be called by the
    /// licensing manager role
    /// @param input_ the input parameters for the framework
    function addFramework(Licensing.SetFramework calldata input_) external onlyRole(AccessControl.LICENSING_MANAGER_ROLE) {
        FrameworkStorage storage framework = _frameworks[input_.id];
        if (framework.paramTags.length() > 0) {
            revert Errors.LicensingFrameworkRepo_FrameworkAlreadyAdded();
        }
        uint256 numParams = input_.paramDefs.length;
        if (numParams > Licensing.MAX_PARAM_TAGS) {
            revert Errors.LicensingFrameworkRepo_TooManyParams();
        }
        for (uint256 i = 0; i < numParams; i++) {
            _addParameter(input_.id, input_.paramDefs[i]);
        }
        framework.textUrl = input_.textUrl;
        emit FrameworkAdded(input_.id, input_.textUrl);
    }

    /// @notice Adds a new parameter to a licensing framework
    /// @param frameworkId_ the ID of the framework
    /// @param paramDef_ the definition of the parameter
    function _addParameter(string calldata frameworkId_, Licensing.ParamDefinition calldata paramDef_) internal {
        FrameworkStorage storage framework = _frameworks[frameworkId_];
        ShortString tag = paramDef_.tag;
        if (framework.paramTags.contains(tag)) {
            revert Errors.LicensingFrameworkRepo_DuplicateParamType();
        }
        framework.paramTags.add(tag);
        _frameworkDefs[keccak256(abi.encode(frameworkId_, tag))] = paramDef_;
        framework.paramDefs.push(paramDef_);
        emit ParamDefinitionAdded(frameworkId_, tag, paramDef_);
    }

    /// Gets the URL to the legal document of a licensing framework
    function getLicenseTextUrl(string calldata frameworkId_) external view returns (string memory) {
        return _frameworks[frameworkId_].textUrl;
    }

    /// Gets the definition of a parameter of a licensing framework at a
    /// given index
    /// @param frameworkId_ the ID of the framework
    /// @param index the index of the parameter
    /// @return the definition of the parameter
    function getParamDefinitionAt(
        string calldata frameworkId_,
        uint256 index
    ) external view returns (Licensing.ParamDefinition memory) {
        FrameworkStorage storage framework = _frameworks[frameworkId_];
        return framework.paramDefs[index];
    }

    /// Gets the amount of parameters of a licensing framework
    /// @param frameworkId_ the ID of the framework
    /// @return the amount of parameters
    function getTotalParameters(string calldata frameworkId_) external view returns (uint256) {
        return _frameworks[frameworkId_].paramDefs.length;
    }

    /// Gets the definition of a parameter of a licensing framework for a
    /// given tag
    /// @param frameworkId_ the ID of the framework
    /// @param tag_ the tag of the parameter
    /// @return the definition of the parameter
    function getParamDefinition(
        string calldata frameworkId_,
        ShortString tag_
    ) external view returns (Licensing.ParamDefinition memory) {
        return _frameworkDefs[keccak256(abi.encode(frameworkId_, tag_))];
    }

    /// Gets all the parameter definitions of a licensing framework
    /// @dev Warning: this function can be expensive if the framework has
    /// many parameters
    /// @param frameworkId_ the ID of the framework
    /// @return the definitions of the parameters
    function getParameterDefs(string calldata frameworkId_) external view returns (Licensing.ParamDefinition[] memory) {
        return _frameworks[frameworkId_].paramDefs;
    }
}

// File: contracts/lib/modules/Licensing.sol



// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

import { BitMask } from "contracts/lib/BitMask.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ShortStrings, ShortString } from "@openzeppelin/contracts/utils/ShortStrings.sol";

/// @title Licensing Module Library
library Licensing {
    using ShortStrings for *;
    /// @notice Enum for the status of a license.
    enum LicenseStatus {
        /// No status has been set.
        Unset,
        /// The license is active. It can be linked with IPAs.
        Active,
        /// The license is pending licensor approval. It can't be linked with IPAs until approved
        /// by the licensor using the LicensingModule (activateLicense)
        PendingLicensorApproval,
        /// The license has been revoked. It can't be linked with IPAs or sublicensed.
        /// Sub-licenses will be considered non active.
        Revoked
    }

    /// @notice Defines the types to encode/decode the parameters of a licensing framework.
    enum ParameterType {
        /// Boolean value
        Bool,
        /// Number value (18 decimals)
        Number,
        /// Address value
        Address,
        /// String value
        String,
        /// Array of ShortString values
        ShortStringArray,
        // uint256 bitmask representing indexes in choices array. ParamDefinition will have the available choices array.
        MultipleChoice
    }

    /// @notice Defines the configuration of the licensor for an IP org.
    enum LicensorConfig {
        /// Null value.
        Unset,
        /// Licensor is the IP org owner, for all licenses.
        IpOrgOwnerAlways,
        /// Licensor will be:
        /// - If parentLicense is provided, the licensee of the parent license.
        /// - If parentLicense is not provided, the Owner of the linked IPA.
        /// - If no parentLicense and no linked IPA, the IP org owner.
        Source
    }

    /// @notice Data struct representing a license agreement minted according to an IP Org Licensing Framework
    struct LicenseData {
        /// License status.
        LicenseStatus status;
        /// If true, other licenses can set this one as parent license, false will prevent it.
        bool derivativesAllowed;
        /// If true, sublicenses can be created by anyone, and the terms of this license will be
        /// applied to the sublicenses. If false, sublicenses can only be created by the licensor.
        bool isReciprocal;
        /// If true, sublicenses will be minted with LicenseStatus.PendingLicensorApproval.
        bool derivativeNeedsApproval;
        /// Address with permission to revoke the license.
        address revoker;
        /// address granting the license
        address licensor;
        /// address of the ip org that produced the terms
        address ipOrg;
        /// The id of the license framework used to mint this license
        ShortString frameworkId;
        /// If the licensee is linked to an IPA, this is the IPA id. 0 otherwise
        uint256 ipaId;
        /// The id of the parent license, if any. 0 otherwise
        uint256 parentLicenseId;
    }

    /// @notice Parameters to mint a license
    struct LicenseCreation {
        /// Array of (tag, value) pairs for the parameters, corresponding to the tags in the
        /// licensing framework.
        ParamValue[] params;
        /// Parent license id, if any. 0 otherwise.
        uint256 parentLicenseId;
        /// Linked IPA id, if any. 0 otherwise.
        uint256 ipaId;
    }

    /// @notice Corresponds to a parameter (licensing term) of a licensing framework.
    struct ParamDefinition {
        /// The parameter id, used to identify the parameter in the license agreement text.
        ShortString tag;
        /// The type of the parameter, used to encode/decode the value
        ParameterType paramType;
        /// Encoded according to paramType, might be empty.
        bytes defaultValue;
        /// If MultipleChoice, String[] of the available choices. Empty bytes otherwise.
        bytes availableChoices;
    }

    /// @notice Corresponds to a value for parameter (licensing term) of a licensing framework.
    struct ParamValue {
        /// The parameter id, used to identify the parameter in the license agreement text.
        ShortString tag;
        /// Encoded according to paramType, might be empty.
        bytes value;
    }

    /// @notice Parameters to set a licensing framework in LicensingFrameworkRepo
    struct SetFramework {
        /// The id of the licensing framework.
        string id;
        /// The URL to the license agreement text.
        string textUrl;
        /// Array of parameter definitions, corresponding to the parameters in
        /// the license agreement text.
        ParamDefinition[] paramDefs;
    }

    /// @notice Parameters to set a licensing framework in an IP org
    struct LicensingConfig {
        /// The id of the licensing framework.
        string frameworkId;
        /// Array of (tag, value) pairs for the parameters, corresponding to the tags in the
        /// licensing framework.
        ParamValue[] params;
        /// Enum with the rules to determine the licensor for this IP org's licenses
        LicensorConfig licensor;
    }

    /// @notice Maximum number of parameters allowed in a licensing framework.
    uint256 public constant MAX_PARAM_TAGS = 255;

    /// Input for IpOrg legal terms configuration in LicensingModule (for now, the only option)
    bytes32 public constant LICENSING_FRAMEWORK_CONFIG = keccak256("LICENSING_FRAMEWORK_CONFIG");
    /// Create license action in LicensingModule
    bytes32 public constant CREATE_LICENSE = keccak256("CREATE_LICENSE");
    /// Activate license action in LicensingModule
    bytes32 public constant ACTIVATE_LICENSE = keccak256("ACTIVATE_LICENSE");
    /// Revoke license action in LicensingModule
    bytes32 public constant LINK_LNFT_TO_IPA = keccak256("LINK_LNFT_TO_IPA");

    /// @notice Returns the string representation of a license status.
    function statusToString(LicenseStatus status_) internal pure returns (string memory) {
        if (status_ == LicenseStatus.Unset) {
            return "Unset";
        } else if (status_ == LicenseStatus.Active) {
            return "Active";
        } else if (status_ == LicenseStatus.PendingLicensorApproval) {
            return "Pending Licensor Approval";
        } else if (status_ == LicenseStatus.Revoked) {
            return "Revoked";
        }
        return "Unknown";
    }

    /// @notice decodes value from a MultipleChoice parameter into the corresponding ShortString[]
    /// with the choices
    /// @param value the encoded value
    /// @param availableChoices the encoded available choices for the parameter, defined in parameter
    /// definition
    function decodeMultipleChoice(
        bytes memory value,
        bytes memory availableChoices
    ) internal pure returns (ShortString[] memory) {
        uint256 mask = abi.decode(value, (uint256));
        uint8[] memory indexes = BitMask.getSetIndexes(mask);
        ShortString[] memory choices = abi.decode(availableChoices, (ShortString[]));
        ShortString[] memory result = new ShortString[](indexes.length);
        for (uint256 i = 0; i < indexes.length; i++) {
            result[i] = choices[indexes[i]];
        }
        return result;
    }

    /// @notice Sets the indexes for the chosen options in a MultipleChoice parameter,
    /// and encodes it into bytes
    /// @param choiceIndexes_ the indexes of the chosen options
    /// @return value the encoded value
    function encodeMultipleChoice(uint8[] memory choiceIndexes_) internal pure returns (bytes memory value) {
        uint256 mask = BitMask.convertToMask(choiceIndexes_);
        return abi.encode(mask);
    }

    /// @notice Validates the value for a parameter, according to its type, meaning it will
    /// decode the value and try to revert if it's not valid.
    /// @dev WARNING: Some validations are not possible to do on chain (like string validation),
    /// so they should be done off chain. Also, Boolean decoded as a Number will be valid.
    /// @param paramDef_ the parameter definition
    /// @param value_ the encoded value
    function validateParamValue(ParamDefinition memory paramDef_, bytes memory value_) internal pure returns (bool) {
        // An empty value signals the parameter is untagged, to trigger default values in the
        // license agreement text, but that's valid
        if (keccak256(value_) == keccak256("")) {
            return true;
        }
        if (paramDef_.paramType == Licensing.ParameterType.Bool) {
            abi.decode(value_, (bool));
            return true;
        } else if (paramDef_.paramType == Licensing.ParameterType.Number && abi.decode(value_, (uint256)) == 0) {
            return false;
        } else if (
            paramDef_.paramType == Licensing.ParameterType.Address &&
            // Not supporting address(0) as a valid value
            abi.decode(value_, (address)) == address(0)
        ) {
            return false;
        } else if (
            paramDef_.paramType == Licensing.ParameterType.String &&
            (keccak256(abi.encodePacked(abi.decode(value_, (string)))) == keccak256(abi.encode(" ")) ||
                keccak256(abi.encodePacked(abi.decode(value_, (string)))) == keccak256(abi.encode("")))
        ) {
            return false;
        } else if (
            paramDef_.paramType == Licensing.ParameterType.ShortStringArray &&
            abi.decode(value_, (ShortString[])).length == 0
        ) {
            return false;
        } else if (
            paramDef_.paramType == Licensing.ParameterType.MultipleChoice &&
            abi.decode(paramDef_.availableChoices, (ShortString[])).length == 0
        ) {
            return false;
        }
        return true;
    }

    /// @notice converts a ShortString[] to a string representation of a JSON array. Used for LicenseRegistry
    /// metadata rendering.
    /// @param ss the ShortString[]
    /// @return the string representation of a JSON array
    function shortStringArrayToJsonArray(ShortString[] memory ss) internal pure returns (string memory) {
        string memory result = "[";
        uint256 len = ss.length;
        for (uint256 i = 0; i < len; i++) {
            ShortString s = ss[i];
            result = string(abi.encodePacked(result, "\"", s.toString(), "\"")); // solhint-disable-line
            if (i != len - 1) {
                result = string(abi.encodePacked(result, ","));
            }
        }
        return string(abi.encodePacked(result, "]"));
    }

    /// @notice decodes value from a parameter into a string representation that can be used
    /// in LicenseRegistry metadata rendering.
    /// @param paramDef_ the parameter definition
    /// @param value_ the encoded value
    /// @return the string representation of the value
    function getDecodedParamString(
        Licensing.ParamDefinition memory paramDef_,
        bytes memory value_
    ) internal pure returns (string memory) {
        if (paramDef_.paramType == Licensing.ParameterType.Bool) {
            return abi.decode(value_, (bool)) ? "true" : "false";
        } else if (paramDef_.paramType == Licensing.ParameterType.Number) {
            return Strings.toString(abi.decode(value_, (uint256)));
        } else if (paramDef_.paramType == Licensing.ParameterType.String) {
            return abi.decode(value_, (string));
        } else if (paramDef_.paramType == Licensing.ParameterType.Address) {
            address addr = abi.decode(value_, (address));
            return Strings.toHexString(uint160(addr), 20);
        } else if (paramDef_.paramType == Licensing.ParameterType.ShortStringArray) {
            ShortString[] memory choices = abi.decode(value_, (ShortString[]));
            return shortStringArrayToJsonArray(choices);
        } else if (paramDef_.paramType == Licensing.ParameterType.MultipleChoice) {
            ShortString[] memory choices = decodeMultipleChoice(value_, paramDef_.availableChoices);
            return shortStringArrayToJsonArray(choices);
        }
        return "";
    }
}

// File: node_modules/@openzeppelin/contracts/utils/ShortStrings.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)

pragma solidity ^0.8.8;

import "./StorageSlot.sol";

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(_FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

// File: node_modules/@openzeppelin/contracts/utils/Multicall.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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.
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function multicall(bytes[] calldata data) external virtual 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;
    }
}

// File: contracts/lib/Errors.sol



// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

/// @title Errors Library
/// @notice Library for all Story Protocol contract errors.
library Errors {
    ////////////////////////////////////////////////////////////////////////////
    //                                  Globals                               //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The provided array may not be empty.
    error EmptyArray();

    /// @notice The arrays may not have a mismatch in length.
    error LengthMismatch();

    /// @notice The provided role does not exist for the given account.
    error MissingRole(bytes32 role, address account);

    /// @notice The caller is not authorized to perform the call.
    error Unauthorized();

    /// @notice The provided interface is not supported.
    error UnsupportedInterface(string name);

    /// @notice The zero address may not be used as input.
    error ZeroAddress();

    /// @notice The amount specified may not be zero.
    error ZeroAmount();

    ////////////////////////////////////////////////////////////////////////////
    //                            BaseModule                                  //
    ////////////////////////////////////////////////////////////////////////////

    error BaseModule_HooksParamsLengthMismatch(uint8 hookType);
    error BaseModule_ZeroIpaRegistry();
    error BaseModule_ZeroLicenseRegistry();
    error BaseModule_OnlyModuleRegistry();

    /// @notice The caller is not authorized to perform this operation.
    error BaseModule_Unauthorized();

    ////////////////////////////////////////////////////////////////////////////
    //                            HookRegistry                                //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The hook is already registered.
    error HookRegistry_RegisteringDuplicatedHook();

    /// @notice This error is thrown when trying to register a hook with the address 0.
    error HookRegistry_RegisteringZeroAddressHook();

    /// @notice This error is thrown when the caller is not IP Org owner.
    error HookRegistry_CallerNotIPOrgOwner();

    /// @notice This error is thrown when trying to register more than the maximum allowed number of hooks.
    error HookRegistry_MaxHooksExceeded();

    /// @notice Hooks configuration array length does not match that of the hooks array.
    error HookRegistry_HooksConfigLengthMismatch();

    /// @notice This error is thrown when the provided index is out of bounds of the hooks array.
    error HookRegistry_IndexOutOfBounds(uint256 hooksIndex);

    /// @notice The module may not be the zero address.
    error HookRegistry_ZeroModuleRegistry();

    /// @notice The provided hook has not been whitelisted.
    error HookRegistry_RegisteringNonWhitelistedHook(address hookAddress);

    ////////////////////////////////////////////////////////////////////////////
    //                      BaseRelationshipProcessor                         //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Call may only be processed by the relationship module.
    error BaseRelationshipProcessor_OnlyRelationshipModule();

    ////////////////////////////////////////////////////////////////////////////
    //                           ModuleRegistry                               //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The selected module has yet to been registered.
    error ModuleRegistry_ModuleNotYetRegistered();

    /// @notice The module depenedency has not yet been registered for the gatway.
    error ModuleRegistry_DependencyNotYetRegistered();

    /// @notice The module depenedency was already registered for the gateway.
    error ModuleRegistry_DependencyAlreadyRegistered();

    /// @notice The caller is not the org owner.
    error ModuleRegistry_CallerNotOrgOwner();

    /// @notice Hook has yet to be registered.
    error ModuleRegistry_HookNotRegistered(string hookKey);

    /// @notice The selected module was already registered.
    error ModuleRegistry_ModuleAlreadyRegistered();

    /// @notice The key of the targeted module does not match the provided key.
    error ModuleRegistry_ModuleKeyMismatch();

    /// @notice The caller is not authorized to call the module dependency.
    error ModuleRegistry_Unauthorized();

    /// @notice The gateway is not valid for registration.
    error ModuleRegistry_InvalidGateway();

    ////////////////////////////////////////////////////////////////////////////
    //                                 CollectModule                          //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Collect module caller is unauthorized.
    error CollectModule_CallerUnauthorized();

    /// @notice Collect NFT has already been initialized.
    error CollectModule_CollectNotYetInitialized();

    /// @notice Collect action is not authorized for the collect module.
    error CollectModule_CollectUnauthorized();

    /// @notice Collect module IP asset is already initialized.
    error CollectModule_IPAssetAlreadyInitialized();

    /// @notice Collect module IP asset does not exist.
    error CollectModule_IPAssetNonExistent();

    /// @notice Collect module provided IP asset registry does not exist.
    error CollectModule_IPOrgNonExistent();

    ////////////////////////////////////////////////////////////////////////////
    //                           CollectPaymentModule                         //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The configured collect module payment amount is invalid.
    error CollectPaymentModule_AmountInvalid();

    /// @notice The ERC-20 transfer failed when processing the payment collect.
    error CollectPaymentModule_ERC20TransferFailed();

    /// @notice The collect ERC-20 transfer was not properly ABI-encoded.
    error CollectPaymentModule_ERC20TransferInvalidABIEncoding();

    /// @notice The collect ERC-20 transfer returned a non-successful value.
    error CollectPaymentModule_ERC20TransferInvalidReturnValue();

    /// @notice Invalid settings were configured for the collect payment module.
    error CollectPaymentModule_InvalidSettings();

    /// @notice Native tokens are not allowed for the configured payment module.
    error CollectPaymentModule_NativeTokenNotAllowed();

    /// @notice Native tokens failed to transfer for the payment collect.
    error CollectPaymentModule_NativeTransferFailed();

    /// @notice Invalid parameters were passed in to the payment collect.
    error CollectPaymentModule_PaymentParamsInvalid();

    /// @notice Insufficient funds were provided for the payment collect.
    error CollectPaymentModule_PaymentInsufficient();

    /// @notice The token provided for the payment collect is invalid.
    error CollectPaymentModule_TokenInvalid();

    ////////////////////////////////////////////////////////////////////////////
    //                                  CollectNFT                            //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Collect NFT has already been initialized.
    error CollectNFT_AlreadyInitialized();

    /// @notice Caller of the Collect NFT is not authorized.
    error CollectNFT_CallerUnauthorized();

    /// @notice Collector address is not valid.
    error CollectNFT_CollectorInvalid();

    /// @notice IP asset bound to the Collect NFT does not exist.
    error CollectNFT_IPAssetNonExistent();

    ////////////////////////////////////////////////////////////////////////////
    //                                   ERC721                               //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Originating address does not own the NFT.
    error ERC721_OwnerInvalid();

    /// @notice Receiving address cannot be the zero address.
    error ERC721_ReceiverInvalid();

    /// @notice Receiving contract does not implement the ERC-721 wallet interface.
    error ERC721_SafeTransferUnsupported();

    /// @notice Sender is not NFT owner, approved address, or owner operator.
    error ERC721_SenderUnauthorized();

    /// @notice Token has already been minted.
    error ERC721_TokenAlreadyMinted();

    /// @notice NFT does not exist.
    error ERC721_TokenNonExistent();

    ////////////////////////////////////////////////////////////////////////////
    //                                 IPAccountImpl                          //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice IP account caller is not the owner.
    error IPAccountImpl_CallerNotOwner();

    ////////////////////////////////////////////////////////////////////////////
    //                               IPAccountRegistry                        //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice IP account implementation does not exist.
    error IPAccountRegistry_NonExistentIpAccountImpl();

    /// @notice IP account initialization failed.
    error IPAccountRegistry_InitializationFailed();

    ////////////////////////////////////////////////////////////////////////////
    //                         UintArrayMask                               //
    ////////////////////////////////////////////////////////////////////////////

    error UintArrayMask_EmptyArray();

    ////////////////////////////////////////////////////////////////////////////
    //                               IPOrg                                    //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice IP identifier is over bounds.
    error IPOrg_IdOverBounds();

    /// @notice Licensing is not configured.
    error IPOrg_LicensingNotConfigured();

    /// @notice IP Org wrapper id does not exist.
    error IPOrg_IdDoesNotExist();

    ////////////////////////////////////////////////////////////////////////////
    //                             IPOrgController                            //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The caller is not the owner of the IP Org Controller.
    error IPOrgController_InvalidOwner();

    /// @notice IP Org does not exist.
    error IPOrgController_IPOrgNonExistent();

    /// @notice The caller is not the authorized IP Org owner.
    error IPOrgController_InvalidIPOrgOwner();

    /// @notice The new owner for an IP Org may not be the zero address.
    error IPOrgController_InvalidNewIPOrgOwner();

    /// @notice The owner transfer has not yet been initialized.
    error IPOrgController_OwnerTransferUninitialized();

    ////////////////////////////////////////////////////////////////////////////
    //                                LibDuration                             //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The caller is not the designated renwer.
    error LibDuration_CallerNotRenewer();

    /// @notice The start time is not valid.
    error LibDuration_InvalidStartTime();

    /// @notice The proposed license is not renewable.
    error LibDuration_NotRenewable();

    /// @notice A zero TTL may not be used for configuration.
    error LibDuration_ZeroTTL();

    ////////////////////////////////////////////////////////////////////////////
    //                       LicensingFrameworkRepo                           //
    ////////////////////////////////////////////////////////////////////////////
    error LicensingFrameworkRepo_FrameworkAlreadyAdded();
    error LicensingFrameworkRepo_DuplicateParamType();
    error LicensingFrameworkRepo_TooManyParams();

    ////////////////////////////////////////////////////////////////////////////
    //                        LicensingModule                                 //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The franchise does not exist.
    error LicensingModule_CallerNotIpOrgOwner();
    error LicensingModule_InvalidConfigType();
    error LicensingModule_InvalidTermCommercialStatus();
    error LicensingModule_IpOrgFrameworkAlreadySet();
    error LicensingModule_DuplicateTermId();
    error LicensingModule_CommercialLicenseNotAllowed();
    error LicensingModule_NonCommercialTermsRequired();
    error LicensingModule_IpOrgNotConfigured();
    error LicensingModule_IpOrgAlreadyConfigured();
    error LicensingModule_ipOrgTermNotFound();
    error LicensingModule_ShareAlikeDisabled();
    error LicensingModule_InvalidAction();
    error LicensingModule_CallerNotLicensor();
    error LicensingModule_ParentLicenseNotActive();
    error LicensingModule_DerivativeNotAllowed();
    error LicensingModule_InvalidIpa();
    error LicensingModule_CallerNotLicenseOwner();
    error LicensingModule_CantFindParentLicenseOrRelatedIpa();
    error LicensingModule_InvalidLicenseeType();
    error LicensingModule_InvalidLicensorType();
    error LicensingModule_InvalidLicensorConfig();
    error LicensingModule_InvalidParamValue();
    error LicensingModule_InvalidParamsLength();
    error LicensingModule_DuplicateParam();
    error LicensingModule_ReciprocalCannotSetParams();
    error LicensingModule_ParamSetByIpOrg();
    error LicensingModule_InvalidInputValue();
    error LicensingModule_IpOrgFrameworkNotSet();

    ////////////////////////////////////////////////////////////////////////////
    //                            LicenseRegistry                             //
    ////////////////////////////////////////////////////////////////////////////

    error LicenseRegistry_UnknownLicenseId();
    error LicenseRegistry_CallerNotLicensingModule();
    error LicenseRegistry_CallerNotRevoker();
    error LicenseRegistry_CallerNotLicensingModuleOrLicensee();
    error LicenseRegistry_CallerNotLicensor();
    error LicenseRegistry_LicenseNotPendingApproval();
    error LicenseRegistry_InvalidLicenseStatus();
    error LicenseRegistry_ParentLicenseNotActive();
    error LicenseRegistry_IPANotActive();
    error LicenseRegistry_LicenseNotActive();
    error LicenseRegistry_LicenseAlreadyLinkedToIpa();

    ////////////////////////////////////////////////////////////////////////////
    //                            RegistrationModule                          //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The caller is not authorized to perform registration.
    error RegistrationModule_CallerNotAuthorized();

    /// @notice The configured caller is invalid.
    error RegistrationModule_InvalidCaller();

    /// @notice The IP asset does not exist.
    error RegistrationModule_IPAssetNonExistent();

    /// @notice The registration module for the IP Org was not yet configured.
    error RegistrationModule_IPOrgNotConfigured();

    /// @notice The registration configuration action is not valid.
    error RegistrationModule_InvalidConfigOperation();

    /// @notice The registration execution action is not valid.
    error RegistrationModule_InvalidExecutionOperation();

    /// @notice IP asset type is not in the list of supported types for
    /// the IP Org.
    error RegistrationModule_InvalidIPAssetType();

    /// @notice IPAsset types provided are more than the maximum allowed.
    error RegistrationModule_TooManyAssetTypes();

    ////////////////////////////////////////////////////////////////////////////
    //                            RelationshipModule                          //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Unable to relate to another franchise.
    error RelationshipModule_CannotRelateToOtherIPOrg();

    /// @notice The intent has already been registered.
    error RelationshipModule_IntentAlreadyRegistered();

    /// @notice The selected TTL is not valid.
    error RelationshipModule_InvalidTTL();

    /// @notice The selected end timestamp is not valid.
    error RelationshipModule_InvalidEndTimestamp();

    /// @notice Relationship does not exist.
    error RelationshipModule_NonExistingRelationship();

    /// @notice The relationship source IP type is not supported.
    error RelationshipModule_UnsupportedRelationshipSrc();

    /// @notice The relationship destination IP type is not supported.
    error RelationshipModule_UnsupportedRelationshipDst();

    /// @notice Trying an unsupported config action
    error RelationshipModule_InvalidConfigOperation();

    /// @notice Unauthorized caller
    error RelationshipModule_CallerNotIpOrgOwner();

    /// @notice Value not on Relatable enum
    error RelationshipModule_InvalidRelatable();

    /// @notice Getting an invalid relationship type
    error RelationshipModule_RelTypeNotSet(string relType);

    /// @notice Relating invalid src addresss
    error RelationshipModule_InvalidSrcAddress();

    /// @notice Relating invalid dst addresss
    error RelationshipModule_InvalidDstAddress();

    /// @notice Relating unsupported src ipOrg asset type
    error RelationshipModule_InvalidSrcId();

    /// @notice Relating unsupported dst ipOrg asset type
    error RelationshipModule_InvalidDstId();

    /// @notice For IPORG_ENTRY - IPORG_ENTRY relationships,
    /// ipOrg address must be set
    error RelationshipModule_IpOrgRelatableCannotBeProtocolLevel();

    /// @notice Index is not found for the asset types of that IP Org.
    error RelationshipModule_UnsupportedIpOrgIndexType();

    ////////////////////////////////////////////////////////////////////////////
    //                                RoyaltyNFT                              //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Mismatch between parity of accounts and their respective allocations.
    error RoyaltyNFT_AccountsAndAllocationsMismatch(uint256 accountsLength, uint256 allocationsLength);

    /// @notice Invalid summation for royalty NFT allocations.
    error RoyaltyNFT_InvalidAllocationsSum(uint32 allocationsSum);

    ////////////////////////////////////////////////////////////////////////////
    //                                  Hook                                  //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice The hook request was not found.
    error Hook_RequestedNotFound();

    /// @notice The sync operation is not supported in Async hooks.
    error Hook_UnsupportedSyncOperation();

    /// @notice The async operation is not supported in Sync hooks.
    error Hook_UnsupportedAsyncOperation();

    /// @notice The callback function can only called by designated callback caller.
    error Hook_OnlyCallbackCallerCanCallback(address current, address expected);

    /// @notice Invalid async request ID.
    error Hook_InvalidAsyncRequestId(bytes32 invalidRequestId);

    /// @notice The address is not the owner of the token.
    error TokenGatedHook_NotTokenOwner(address tokenAddress, address ownerAddress);

    error Hook_AsyncHookError(bytes32 requestId, string reason);

    /// @notice Invalid Hook configuration.
    error Hook_InvalidHookConfig(string reason);

    ////////////////////////////////////////////////////////////////////////////
    //                       LicensorApprovalHook                             //
    ////////////////////////////////////////////////////////////////////////////

    error LicensorApprovalHook_ApprovalAlreadyRequested();
    error LicensorApprovalHook_InvalidLicensor();
    error LicensorApprovalHook_InvalidLicenseId();
    error LicensorApprovalHook_NoApprovalRequested();
    error LicensorApprovalHook_InvalidResponseStatus();
}

// File: contracts/access-control/AccessControlled.sol



// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

// solhint-disable-next-line max-line-length
import { ERC165CheckerUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol";
import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";

import { AccessControl } from "contracts/lib/AccessControl.sol";
import { IAccessControlled } from "contracts/interfaces/access-control/IAccessControlled.sol";
import { Errors } from "contracts/lib/Errors.sol";

/// @title Access Controlled Contract
/// @notice This contract is to be inherited by any protocol components that require granular
///         roles for execution, as defined by the Access Control Singleton contract. Later on,
///         this contract will be deprecated in favor of authorization through te module registry.
abstract contract AccessControlled is IAccessControlled {
    using ERC165CheckerUpgradeable for address;

    /// @notice Pointer to the global Access Control Singleton for protocol auth.
    IAccessControl private _accessControl;

    /// @notice Checks if msg.sender has role `role`, reverts otherwise.
    /// @param role_ The role being checked for, set by the Access Control Singleton.
    modifier onlyRole(bytes32 role_) {
        if (!_hasRole(role_, msg.sender)) {
            revert Errors.MissingRole(role_, msg.sender);
        }
        _;
    }

    /// @notice Instantiates a new Access Controlled contract.
    /// @param accessControl_ The global Access Control Singleton contract address.
    constructor(address accessControl_) {
        if (!accessControl_.supportsInterface(type(IAccessControl).interfaceId))
            revert Errors.UnsupportedInterface("IAccessControl");
        _accessControl = IAccessControl(accessControl_);
        emit AccessControlUpdated(accessControl_);
    }

    /// @notice Sets the Access Control Singleton used for authorization.
    /// @param accessControl_ The address of the new Access Control Singleton.
    function setAccessControl(address accessControl_) public onlyRole(AccessControl.PROTOCOL_ADMIN_ROLE) {
        if (!accessControl_.supportsInterface(type(IAccessControl).interfaceId))
            revert Errors.UnsupportedInterface("IAccessControl");
        _accessControl = IAccessControl(accessControl_);
        emit AccessControlUpdated(accessControl_);
    }

    /// @dev Checks if an account `account_` has role `role_` assigned.
    /// @param role_ The role being checked for as defined by the Access Control Singlton.
    /// @param account_ The address whose role permissions are being checked for.
    /// @return return True if the account has the role, False otherwise.
    function _hasRole(bytes32 role_, address account_) internal view returns (bool) {
        return _accessControl.hasRole(role_, account_);
    }
}

// File: contracts/lib/AccessControl.sol



// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.19;

/// @title Access Control Library
/// @notice Library for access control helpers and protocol role definitions.
///         These roles are used by the AccessControlSingleton, accessed by AccessControlled contracts.
library AccessControl {
    // Default admin role as per OZ AccessControl system. All other roles stem from this.
    bytes32 public constant PROTOCOL_ADMIN_ROLE = bytes32(0);

    // Role that can upgrade UUPS contracts or Beacon Proxies
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

    // Role for managing protocol-wide and IP Org localized relationships.
    bytes32 public constant RELATIONSHIP_MANAGER_ROLE = keccak256("RELATIONSHIP_MANAGER_ROLE");

    // Role that can perform admin tasks on the Licensing Module contracts (setNonCommercialLicenseURI)
    bytes32 public constant LICENSING_MANAGER_ROLE = keccak256("LICENSING_MANAGER_ROLE");

    // Role that can add new modules to the Module Registry
    bytes32 public constant MODULE_REGISTRAR_ROLE = keccak256("MODULE_REGISTRAR_ROLE");

    // Role that can execute modules
    bytes32 public constant MODULE_EXECUTOR_ROLE = keccak256("MODULE_EXECUTOR_ROLE");

    // Role that can execute Hooks
    bytes32 public constant HOOK_CALLER_ROLE = keccak256("HOOK_CALLER_ROLE");

}

// File: contracts/utils/FixedSet.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol";

/// @title FixedSet Library
/// @notice Fork of OZ's set data structures library with the following changs:
/// - Values cannot be removed from the set for order preservation
/// - The library allows obtaining indexes of values
/// - Adds ShortString as a data type
library FixedSet {
    using ShortStrings for *;

    uint256 internal constant INDEX_NOT_FOUND = type(uint256).max;

    /// @notice Data structure for composing a fixed set.
    struct Set {
        // Array for storing values within the fixed set.
        bytes32[] _values;
        // One-based index of the set value (0 is a sentinel valu).
        mapping(bytes32 => uint256) _indexes;
    }

    /// @dev Adds a value to a set.
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // Value is stored at values.length due to one-indexing.
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /// @dev Checks whether a value is contained in the set.
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /// @dev Returns the length of the set.
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /// @dev Returns the index of a value within the set.
    function _indexOf(Set storage set, bytes32 value) private view returns (uint256) {
        uint256 index = set._indexes[value];
        return index == 0 ? INDEX_NOT_FOUND : index - 1;
    }

    /// @dev Returns the value stored at the one-indexed positioned within the set.
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /// @dev Returns the entire fixed set as a bytes32-array.
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    ////////////////////////////////////////////////////////////////////////////
    //                            Bytes32Set                                  //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Struct for composing fixed sets of bytes32 objects.
    struct Bytes32Set {
        Set _inner;
    }

    /// @dev Adds a value to the bytes32 set.
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /// @dev Checks whether a bytes32 set contains a value.
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /// @dev Gets the length of the bytes32 set.
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /// @dev Gets the value stored at the one-indexed position within the set.
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /// @dev Returns the one-indexed position of the value within the set.
    function indexOf(Bytes32Set storage set, bytes32 value) internal view returns (uint256) {
        return _indexOf(set._inner, value);
    }

    /// @dev Returns the entire set of bytes32 objects as an array.
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////
    //                            ShortStringSet                               //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Fixed set data structure for representing SortStrings.
    struct ShortStringSet {
        Set _inner;
    }

    /// @dev Adds a value to the ShortString data set.
    function add(ShortStringSet storage set, ShortString value) internal returns (bool) {
        return _add(set._inner, ShortString.unwrap(value));
    }

    /// @dev Checks whether a ShortString set contains a value.
    function contains(ShortStringSet storage set, ShortString value) internal view returns (bool) {
        return _contains(set._inner, ShortString.unwrap(value));
    }

    /// @dev Returns the length of the ShortString set.
    function length(ShortStringSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /// @dev Returns the value stored at the one-indexed position in the set.
    function at(ShortStringSet storage set, uint256 index) internal view returns (ShortString) {
        return ShortString.wrap(_at(set._inner, index));
    }

    /// @dev Returns the index of the value within the ShortString set.
    function indexOf(ShortStringSet storage set, ShortString value) internal view returns (uint256) {
        return _indexOf(set._inner, ShortString.unwrap(value));
    }

    /// @dev Returns the entire ShortString data set.
    function values(ShortStringSet storage set) internal view returns (ShortString[] memory) {
        bytes32[] memory store = _values(set._inner);
        ShortString[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////
    //                            AddressSet                                  //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Fixed set data structure for representing addresses.
    struct AddressSet {
        Set _inner;
    }

    /// @dev Adds a value to the fixed address set.
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /// @dev Checks whether a fixed address set contains a value.
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /// @dev Returns the length of the fixed address set.
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /// @dev Returns the value stored at the one-indexed position in the set.
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /// @dev Gets the index of an address within the fixed set.
    function indexOf(AddressSet storage set, address value) internal view returns (uint256) {
        return _indexOf(set._inner, bytes32(uint256(uint160(value))));
    }

    /// @dev Returns the entire suite of addresses stored in the set.
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    ////////////////////////////////////////////////////////////////////////////
    //                            UintSet                                     //
    ////////////////////////////////////////////////////////////////////////////

    /// @notice Fixed set data structure for storing uint256 numbers.
    struct UintSet {
        Set _inner;
    }

    /// @dev Adds a uint256 value into the fixed set.
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /// @dev Checks whether the fixed set contains a uint256 value.
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /// @dev Returns the length of the uint256 set.
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /// @dev Returns the value stored at a one-indexed position within the set.
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /// @dev Returns the index of a uint256 value within the set.
    function indexOf(UintSet storage set, uint256 value) internal view returns (uint256) {
        return _indexOf(set._inner, bytes32(value));
    }

    /// @dev Returns the entire suite of uint256 values within the set.
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// File: contracts/lib/BitMask.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**
 * @notice Based on OpenZeppelin's BitMap, this library is used to encode a set of indexes in a compact way.
 * Instead of using a storage type like OZ, where they use a mapping(uint256 => uint256) for large numbers of values,
 * this library limts it to a 256 values in a single uint256.
 */
library BitMask {
    /// Returns whether the bit at `index` is set.
    function isSet(uint256 mask_, uint8 index_) internal pure returns (bool) {
        uint256 indexMask = 1 << (index_ & 0xff);
        return mask_ & indexMask != 0;
    }

    /// Sets the bit at `index` to the boolean `value`.
    function setTo(uint256 mask_, uint256 index_, bool value_) internal pure returns (uint256) {
        if (value_) {
            return set(mask_, index_);
        } else {
            return unset(mask_, index_);
        }
    }

    /// Sets the bit at `index`.
    function set(uint256 mask_, uint256 index_) internal pure returns (uint256) {
        uint256 indexMask = 1 << (index_ & 0xff);
        return mask_ |= indexMask;
    }

    /// Unsets the bit at `index`.
    function unset(uint256 mask_, uint256 index_) internal pure returns (uint256) {
        uint256 indexMask = 1 << (index_ & 0xff);
        return mask_ &= ~indexMask;
    }

    /// Gets the uint8 from the bitmask as an array
    function getSetIndexes(uint256 mask_) internal pure returns (uint8[] memory) {
        // Count the number of set bits to allocate the array size
        uint256 count;
        for (uint8 i = 0; i < 255; ++i) {
            if (isSet(mask_, i)) {
                ++count;
            }
        }
        uint8[] memory setBitIndexes = new uint8[](count);
        // Fill the array with indices of set bits
        uint256 index = 0;
        for (uint8 i = 0; i < 255; ++i) {
            if (isSet(mask_, i)) {
                setBitIndexes[index] = i;
                ++index;
            }
        }
        return setBitIndexes;
    }

    /// Converts an array of uint8 to a bit mask
    function convertToMask(uint8[] memory indexes_) internal pure returns (uint256) {
        uint256 mask = 0;
        for (uint256 i = 0; i < indexes_.length; ) {
            mask |= 1 << (uint256(indexes_[i]) & 0xff);
            unchecked {
                i++;
            }
        }
        return mask;
    }
}

// File: node_modules/@openzeppelin/contracts/utils/Strings.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// File: node_modules/@openzeppelin/contracts/utils/StorageSlot.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

// File: node_modules/@openzeppelin/contracts/utils/Address.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// File: node_modules/@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165CheckerUpgradeable {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface.
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            supportsERC165InterfaceUnchecked(account, type(IERC165Upgradeable).interfaceId) &&
            !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     *
     * _Available since v3.4._
     */
    function getSupportedInterfaces(
        address account,
        bytes4[] memory interfaceIds
    ) internal view returns (bool[] memory) {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     *
     * Some precompiled contracts will falsely indicate support for a given interface, so caution
     * should be exercised when using this function.
     *
     * Interface identification is specified in ERC-165.
     */
    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeWithSelector(IERC165Upgradeable.supportsInterface.selector, interfaceId);

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}

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

// File: contracts/interfaces/access-control/IAccessControlled.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title Access Controlled Interface
/// @notice This interface must be implemented by all protocol components that require
///         to be authorized via the global Access Control Singleton contract. This
///         initially includes all modules and hooks contracts, but will later be
///         sunset in favor of central authorization via the module registry.
interface IAccessControlled {
    /// @notice Emits when the global Access Control Singleton contract is updated.
    /// @param accessControl Address of the protocol-wide Access Control singleton contract.
    event AccessControlUpdated(address indexed accessControl);

    /// @notice Sets the Access Control Singleton instance.
    /// @param accessControl_ address of the new instance of the Access Control Singleton.
    function setAccessControl(address accessControl_) external;
}

// File: node_modules/@openzeppelin/contracts/utils/math/Math.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// File: node_modules/@openzeppelin/contracts/utils/math/SignedMath.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// File: node_modules/@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.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 IERC165Upgradeable {
    /**
     * @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);
}