creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721Upgradeable.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721MetadataUpgradeable is IERC721Upgradeable {\n    /**\n     * @dev Returns the token collection name.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the token collection symbol.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n     */\n    function tokenURI(uint256 tokenId) external view returns (string memory);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721ReceiverUpgradeable {\n    /**\n     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n     * by `operator` from `from`, this function is called.\n     *\n     * It must return its Solidity selector to confirm the token transfer.\n     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n     *\n     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\n     */\n    function onERC721Received(\n        address operator,\n        address from,\n        uint256 tokenId,\n        bytes calldata data\n    ) external returns (bytes4);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165Upgradeable.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721Upgradeable is IERC165Upgradeable {\n    /**\n     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n     */\n    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n     */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n     * @dev Returns the number of tokens in ``owner``'s account.\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Transfers `tokenId` token from `from` to `to`.\n     *\n     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721\n     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n     * understand this adds an external call which potentially creates a reentrancy vulnerability.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n     * The approval is cleared when the token is transferred.\n     *\n     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n     *\n     * Requirements:\n     *\n     * - The caller must own the token or be an approved operator.\n     * - `tokenId` must exist.\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address to, uint256 tokenId) external;\n\n    /**\n     * @dev Approve or remove `operator` as an operator for the caller.\n     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n     *\n     * Requirements:\n     *\n     * - The `operator` cannot be the caller.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function setApprovalForAll(address operator, bool approved) external;\n\n    /**\n     * @dev Returns the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\n\n    /**\n     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n     *\n     * See {setApprovalForAll}\n     */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"
      },
      "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165Upgradeable {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Create2.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Create2.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.\n * `CREATE2` can be used to compute in advance the address where a smart\n * contract will be deployed, which allows for interesting new mechanisms known\n * as 'counterfactual interactions'.\n *\n * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more\n * information.\n */\nlibrary Create2 {\n    /**\n     * @dev Deploys a contract using `CREATE2`. The address where the contract\n     * will be deployed can be known in advance via {computeAddress}.\n     *\n     * The bytecode for a contract can be obtained from Solidity with\n     * `type(contractName).creationCode`.\n     *\n     * Requirements:\n     *\n     * - `bytecode` must not be empty.\n     * - `salt` must have not been used for `bytecode` already.\n     * - the factory must have a balance of at least `amount`.\n     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.\n     */\n    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {\n        require(address(this).balance >= amount, \"Create2: insufficient balance\");\n        require(bytecode.length != 0, \"Create2: bytecode length is zero\");\n        /// @solidity memory-safe-assembly\n        assembly {\n            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)\n        }\n        require(addr != address(0), \"Create2: Failed on deploy\");\n    }\n\n    /**\n     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the\n     * `bytecodeHash` or `salt` will result in a new destination address.\n     */\n    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {\n        return computeAddress(salt, bytecodeHash, address(this));\n    }\n\n    /**\n     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at\n     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.\n     */\n    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let ptr := mload(0x40) // Get free memory pointer\n\n            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |\n            // |-------------------|---------------------------------------------------------------------------|\n            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |\n            // | salt              |                                      BBBBBBBBBBBBB...BB                   |\n            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |\n            // | 0xFF              |            FF                                                             |\n            // |-------------------|---------------------------------------------------------------------------|\n            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |\n            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |\n\n            mstore(add(ptr, 0x40), bytecodeHash)\n            mstore(add(ptr, 0x20), salt)\n            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes\n            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff\n            mstore8(start, 0xff)\n            addr := keccak256(start, 85)\n        }\n    }\n}\n"
      },
      "contracts/diamond/DiamondLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport { IAccessControl } from \"../interfaces/IAccessControl.sol\";\nimport { IDiamondCut } from \"../interfaces/diamond/IDiamondCut.sol\";\n\n/**\n * @title DiamondLib\n *\n * @notice Provides Diamond storage slot and supported interface checks.\n *\n * @notice Based on Nick Mudge's gas-optimized diamond-2 reference,\n * with modifications to support role-based access and management of\n * supported interfaces. Also added copious code comments throughout.\n *\n * Reference Implementation  : https://github.com/mudgen/diamond-2-hardhat\n * EIP-2535 Diamond Standard : https://eips.ethereum.org/EIPS/eip-2535\n *\n * N.B. Facet management functions from original `DiamondLib` were refactored/extracted\n * to JewelerLib, since business facets also use this library for access control and\n * managing supported interfaces.\n *\n * @author Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)\n * @author Cliff Hall <cliff@futurescale.com> (https://twitter.com/seaofarrows)\n */\nlibrary DiamondLib {\n    bytes32 internal constant DIAMOND_STORAGE_POSITION = keccak256(\"diamond.standard.diamond.storage\");\n\n    struct DiamondStorage {\n        // Maps function selectors to the facets that execute the functions\n        // and maps the selectors to their position in the selectorSlots array.\n        // func selector => address facet, selector position\n        mapping(bytes4 => bytes32) facets;\n        // Array of slots of function selectors.\n        // Each slot holds 8 function selectors.\n        mapping(uint256 => bytes32) selectorSlots;\n        // The number of function selectors in selectorSlots\n        uint16 selectorCount;\n        // Used to query if a contract implement is an interface.\n        // Used to implement ERC-165.\n        mapping(bytes4 => bool) supportedInterfaces;\n        // The Boson Protocol AccessController\n        IAccessControl accessController;\n    }\n\n    /**\n     * @notice Gets the Diamond storage slot.\n     *\n     * @return ds - Diamond storage slot cast to DiamondStorage\n     */\n    function diamondStorage() internal pure returns (DiamondStorage storage ds) {\n        bytes32 position = DIAMOND_STORAGE_POSITION;\n        assembly {\n            ds.slot := position\n        }\n    }\n\n    /**\n     * @notice Adds a supported interface to the Diamond.\n     *\n     * @param _interfaceId - the interface to add\n     */\n    function addSupportedInterface(bytes4 _interfaceId) internal {\n        // Get the DiamondStorage struct\n        DiamondStorage storage ds = diamondStorage();\n\n        // Flag the interfaces as supported\n        ds.supportedInterfaces[_interfaceId] = true;\n    }\n\n    /**\n     * @notice Removes a supported interface from the Diamond.\n     *\n     * @param _interfaceId - the interface to remove\n     */\n    function removeSupportedInterface(bytes4 _interfaceId) internal {\n        // Get the DiamondStorage struct\n        DiamondStorage storage ds = diamondStorage();\n\n        // Flag the interfaces as unsupported\n        ds.supportedInterfaces[_interfaceId] = false;\n    }\n\n    /**\n     * @notice Checks if a specific interface is supported.\n     * Implementation of ERC-165 interface detection standard.\n     *\n     * @param _interfaceId - the sighash of the given interface\n     * @return - whether or not the interface is supported\n     */\n    function supportsInterface(bytes4 _interfaceId) internal view returns (bool) {\n        // Get the DiamondStorage struct\n        DiamondStorage storage ds = diamondStorage();\n\n        // Return the value\n        return ds.supportedInterfaces[_interfaceId];\n    }\n}\n"
      },
      "contracts/domain/BosonConstants.sol": {
        "content": "import \"./BosonTypes.sol\";\n\n// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\n// Access Control Roles\nbytes32 constant ADMIN = keccak256(\"ADMIN\"); // Role Admin\nbytes32 constant PAUSER = keccak256(\"PAUSER\"); // Role for pausing the protocol\nbytes32 constant PROTOCOL = keccak256(\"PROTOCOL\"); // Role for facets of the ProtocolDiamond\nbytes32 constant CLIENT = keccak256(\"CLIENT\"); // Role for clients of the ProtocolDiamond\nbytes32 constant UPGRADER = keccak256(\"UPGRADER\"); // Role for performing contract and config upgrades\nbytes32 constant FEE_COLLECTOR = keccak256(\"FEE_COLLECTOR\"); // Role for collecting fees from the protocol\n\n// Generic\nuint256 constant HUNDRED_PERCENT = 10000; // 100% in basis points\n\n// Pause Handler\nuint256 constant ALL_REGIONS_MASK = (1 << (uint256(type(BosonTypes.PausableRegion).max) + 1)) - 1;\n\n// Reentrancy guard\nuint256 constant NOT_ENTERED = 1;\nuint256 constant ENTERED = 2;\n\n// Twin handler\nuint256 constant SINGLE_TWIN_RESERVED_GAS = 160000;\nuint256 constant MINIMAL_RESIDUAL_GAS = 230000;\n\n// Config related\nbytes32 constant VOUCHER_PROXY_SALT = keccak256(abi.encodePacked(\"BosonVoucherProxy\"));\n\n// Funds related\nstring constant NATIVE_CURRENCY = \"Native currency\";\nstring constant TOKEN_NAME_UNSPECIFIED = \"Token name unavailable\";\n\n// EIP712Lib\nstring constant PROTOCOL_NAME = \"Boson Protocol\";\nstring constant PROTOCOL_VERSION = \"V2\";\nbytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256(\n    bytes(\"EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)\")\n);\n\n// BosonVoucher\nstring constant VOUCHER_NAME = \"Boson Voucher (rNFT)\";\nstring constant VOUCHER_SYMBOL = \"BOSON_VOUCHER_RNFT\";\n\n// Meta Transactions - Error\nstring constant FUNCTION_CALL_NOT_SUCCESSFUL = \"Function call not successful\";\n\n// External contracts errors\nstring constant OWNABLE_ZERO_ADDRESS = \"Ownable: new owner is the zero address\"; // exception message from OpenZeppelin Ownable\nstring constant ERC721_INVALID_TOKEN_ID = \"ERC721: invalid token ID\"; // exception message from OpenZeppelin ERC721\n\n// Meta Transactions - Structs\nbytes32 constant META_TRANSACTION_TYPEHASH = keccak256(\n    bytes(\n        \"MetaTransaction(uint256 nonce,address from,address contractAddress,string functionName,bytes functionSignature)\"\n    )\n);\nbytes32 constant OFFER_DETAILS_TYPEHASH = keccak256(\"MetaTxOfferDetails(address buyer,uint256 offerId)\");\nbytes32 constant META_TX_COMMIT_TO_OFFER_TYPEHASH = keccak256(\n    \"MetaTxCommitToOffer(uint256 nonce,address from,address contractAddress,string functionName,MetaTxOfferDetails offerDetails)MetaTxOfferDetails(address buyer,uint256 offerId)\"\n);\nbytes32 constant CONDITIONAL_OFFER_DETAILS_TYPEHASH = keccak256(\n    \"MetaTxConditionalOfferDetails(address buyer,uint256 offerId,uint256 tokenId)\"\n);\nbytes32 constant META_TX_COMMIT_TO_CONDITIONAL_OFFER_TYPEHASH = keccak256(\n    \"MetaTxCommitToConditionalOffer(uint256 nonce,address from,address contractAddress,string functionName,MetaTxConditionalOfferDetails offerDetails)MetaTxConditionalOfferDetails(address buyer,uint256 offerId,uint256 tokenId)\"\n);\nbytes32 constant EXCHANGE_DETAILS_TYPEHASH = keccak256(\"MetaTxExchangeDetails(uint256 exchangeId)\");\nbytes32 constant META_TX_EXCHANGE_TYPEHASH = keccak256(\n    \"MetaTxExchange(uint256 nonce,address from,address contractAddress,string functionName,MetaTxExchangeDetails exchangeDetails)MetaTxExchangeDetails(uint256 exchangeId)\"\n);\nbytes32 constant FUND_DETAILS_TYPEHASH = keccak256(\n    \"MetaTxFundDetails(uint256 entityId,address[] tokenList,uint256[] tokenAmounts)\"\n);\nbytes32 constant META_TX_FUNDS_TYPEHASH = keccak256(\n    \"MetaTxFund(uint256 nonce,address from,address contractAddress,string functionName,MetaTxFundDetails fundDetails)MetaTxFundDetails(uint256 entityId,address[] tokenList,uint256[] tokenAmounts)\"\n);\nbytes32 constant DISPUTE_RESOLUTION_DETAILS_TYPEHASH = keccak256(\n    \"MetaTxDisputeResolutionDetails(uint256 exchangeId,uint256 buyerPercentBasisPoints,bytes32 sigR,bytes32 sigS,uint8 sigV)\"\n);\nbytes32 constant META_TX_DISPUTE_RESOLUTIONS_TYPEHASH = keccak256(\n    \"MetaTxDisputeResolution(uint256 nonce,address from,address contractAddress,string functionName,MetaTxDisputeResolutionDetails disputeResolutionDetails)MetaTxDisputeResolutionDetails(uint256 exchangeId,uint256 buyerPercentBasisPoints,bytes32 sigR,bytes32 sigS,uint8 sigV)\"\n);\n\n// Function names\nstring constant COMMIT_TO_OFFER = \"commitToOffer(address,uint256)\";\nstring constant COMMIT_TO_CONDITIONAL_OFFER = \"commitToConditionalOffer(address,uint256,uint256)\";\nstring constant CANCEL_VOUCHER = \"cancelVoucher(uint256)\";\nstring constant REDEEM_VOUCHER = \"redeemVoucher(uint256)\";\nstring constant COMPLETE_EXCHANGE = \"completeExchange(uint256)\";\nstring constant WITHDRAW_FUNDS = \"withdrawFunds(uint256,address[],uint256[])\";\nstring constant RETRACT_DISPUTE = \"retractDispute(uint256)\";\nstring constant RAISE_DISPUTE = \"raiseDispute(uint256)\";\nstring constant ESCALATE_DISPUTE = \"escalateDispute(uint256)\";\nstring constant RESOLVE_DISPUTE = \"resolveDispute(uint256,uint256,bytes32,bytes32,uint8)\";\n"
      },
      "contracts/domain/BosonErrors.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport { BosonTypes } from \"./BosonTypes.sol\";\n\ninterface BosonErrors {\n    // Pause related\n    // Trying to unpause a protocol when it's not paused\n    error NotPaused();\n    // Whenever a region is paused, and a method from that region is called\n    error RegionPaused(BosonTypes.PausableRegion region);\n\n    // General\n    // Input parameter of type address is zero address\n    error InvalidAddress();\n    // Exchange or dispute is in different state than expected when certain action is called\n    error InvalidState();\n    // Two or more array parameters with different lengths\n    error ArrayLengthMismatch();\n\n    // Reentrancy guard\n    // Reentrancy guard is active and second call to protocol is made\n    error ReentrancyGuard();\n\n    // Protocol initialization related\n    // Trying to initialize the facet when it's already initialized\n    error AlreadyInitialized(); // ToDo consider adding the facet to the error message\n    // Initialization of some facet failed\n    error ProtocolInitializationFailed(); // ToDo consider adding the facet to the error message\n    // Trying to initialize the protocol with empty version\n    error VersionMustBeSet();\n    // Length of _addresses and _calldata arrays do not match\n    error AddressesAndCalldataLengthMismatch(); // ToDo consider reusing ArrayLengthMismatch\n    // The new protocol version is not subsequent to the current one\n    error WrongCurrentVersion();\n    // Initialization can be done only through proxy\n    error DirectInitializationNotAllowed();\n    // Initialization of v2.3.0 can be done only if not twin exists\n    error TwinsAlreadyExist();\n\n    // Access related\n    // ToDo consider having a single error, with a parameter for the role\n    // Caller is not authorized to call the method\n    error AccessDenied();\n    // Caller is not entitiy's assistant\n    error NotAssistant();\n    // Caller is not entitiy's admin\n    error NotAdmin();\n    // Caller is not entitiy's admin and assistant\n    error NotAdminAndAssistant();\n    // Caller is neither the buyer or the seller involved in the exchange\n    error NotBuyerOrSeller();\n    // Caller is not the owner of the voucher\n    error NotVoucherHolder();\n    // Caller is not the buyer\n    error NotBuyerWallet();\n    // Caller is not the agent\n    error NotAgentWallet();\n    // Caller is not dispute resolver assistant\n    error NotDisputeResolverAssistant();\n    // Supplied clerk is not zero address\n    error ClerkDeprecated();\n\n    // Account-related\n    // Entity must be active\n    error MustBeActive();\n    // Seller's address cannot be already used in another seller\n    error SellerAddressMustBeUnique();\n    // Buyer's address cannot be already used in another buyer\n    error BuyerAddressMustBeUnique();\n    // DR's address cannot be already used in another DR\n    error DisputeResolverAddressMustBeUnique();\n    // Agent's address cannot be already used in another agent\n    error AgentAddressMustBeUnique();\n    // Seller does not exist\n    error NoSuchSeller();\n    // Buyer does not exist\n    error NoSuchBuyer();\n    // Dispute resolver does not exist\n    error NoSuchDisputeResolver();\n    // Agent does not exist\n    error NoSuchAgent();\n    // Buyer is involved in an non-finalized exchange\n    error WalletOwnsVouchers();\n    // Escalation period is not greater than zero or is more than the max allowed\n    error InvalidEscalationPeriod();\n    // Action would remove the last supported fee from the DR (must always have at least one)\n    error InexistentDisputeResolverFees();\n    // Trying to add a fee that already exists\n    error DuplicateDisputeResolverFees();\n    // Trying to add a fee with non-zero amount\n    error FeeAmountNotYetSupported();\n    // Trying to remove a fee that does not exist\n    error DisputeResolverFeeNotFound();\n    // Trying to approve a seller that is already approved (list of sellers that DR will handle disputes for)\n    error SellerAlreadyApproved();\n    // Trying to assing a DR that had not approved the seller\n    error SellerNotApproved();\n    // Trying to add or removed 0 sellers\n    error InexistentAllowedSellersList();\n    // Custom auth token is not yet supported\n    error InvalidAuthTokenType();\n    // Seller must use either and address or auth token for authentication, but not both\n    error AdminOrAuthToken();\n    // A single auth token can only be used by one seller\n    error AuthTokenMustBeUnique();\n    // Sum of protocol and agent fee exceed the max allowed fee\n    error InvalidAgentFeePercentage();\n    // Trying to finalize the update, while it's not even started\n    error NoPendingUpdateForAccount();\n    // Only the account itself can finalize the update\n    error UnauthorizedCallerUpdate();\n    // Trying to update the account with the same values\n    error NoUpdateApplied();\n    // Creating a seller's collection failed\n    error CloneCreationFailed();\n    // Seller's salt is already used by another seller\n    error SellerSaltNotUnique();\n\n    // Offer related\n    // Offer does not exist\n    error NoSuchOffer();\n    // Offer finishes in the past or it starts after it finishes\n    error InvalidOfferPeriod();\n    // Buyer cancellation penalty is higher than the item price\n    error InvalidOfferPenalty();\n    // New offer must be actiove\n    error OfferMustBeActive();\n    // Offer can be added to same group only once\n    error OfferMustBeUnique();\n    // Offer has been voided\n    error OfferHasBeenVoided();\n    // Current timestamp is higher than offer's expiry timestamp\n    error OfferHasExpired();\n    // Current timestamp is lower than offer's start timestamp\n    error OfferNotAvailable();\n    // Offer's quantity available is zero\n    error OfferSoldOut();\n    // Buyer is not allowed to commit to the offer (does not meet the token gating requirements)\n    error CannotCommit();\n    // Bundle cannot be created since exchganes for offer exist already\n    error ExchangeForOfferExists();\n    // Voucher must have either a fixed expiry or a fixed redeemable period, not both\n    error AmbiguousVoucherExpiry();\n    // Redemption period starts after it ends or it ends before offer itself expires\n    error InvalidRedemptionPeriod();\n    // Dispute period is less than minimal dispute period allowed\n    error InvalidDisputePeriod();\n    // Resolution period is not within the allowed range or it's being misconfigured (minimal > maximal)\n    error InvalidResolutionPeriod();\n    // Dispute resolver does not exist or is not active\n    error InvalidDisputeResolver();\n    // Quantity available is zero\n    error InvalidQuantityAvailable();\n    // Chose DR does not support the fees in the chosen exchange token\n    error DRUnsupportedFee();\n    // Sum of protocol and agent fee exceeds the max allowed fee\n    error AgentFeeAmountTooHigh();\n    // Sum of protocol and agent fee exceeds the seller defined max fee\n    error TotalFeeExceedsLimit();\n    // Collection does not exist\n    error NoSuchCollection();\n    // Royalty recipient is not allow listed for the seller\n    error InvalidRoyaltyRecipient();\n    // Total royality fee exceeds the max allowed\n    error InvalidRoyaltyPercentage();\n    // Specified royalty recipient already added\n    error RecipientNotUnique();\n    // Trying to access an out of bounds royalty recipient\n    error InvalidRoyaltyRecipientId();\n    // Array of royalty recipients is not sorted by id\n    error RoyaltyRecipientIdsNotSorted();\n    // Trying to remove the default recipient (treasury)\n    error CannotRemoveDefaultRecipient();\n    // Supplying too many Royalty info structs\n    error InvalidRoyaltyInfo();\n    // Trying to change the default recipient address (treasury)\n    error WrongDefaultRecipient();\n    // Price discovery offer has non zero price\n    error InvalidPriceDiscoveryPrice();\n\n    // Group related\n    // Group does not exist\n    error NoSuchGroup();\n    // Offer is not in a group\n    error OfferNotInGroup();\n    // Group remains the same\n    error NothingUpdated();\n    // There is a logical error in the group's condition parameters or it's not supported yet\n    error InvalidConditionParameters();\n    // Group does not have a condition\n    error GroupHasNoCondition();\n    // Group has a condition\n    error GroupHasCondition();\n    // User exhaused the number of commits allowed for the group\n    error MaxCommitsReached();\n    // The supplied token id is outside the condition's range\n    error TokenIdNotInConditionRange();\n    // ERC20 and ERC721 require zero token id\n    error InvalidTokenId();\n\n    // Exchange related\n    // Exchange does not exist\n    error NoSuchExchange();\n    // Exchange cannot be completed yet\n    error DisputePeriodNotElapsed();\n    // Current timestamp is outside the voucher's redeemable period\n    error VoucherNotRedeemable();\n    // New expiration date is earlier than existing expiration date\n    error VoucherExtensionNotValid();\n    // Voucher cannot be expired yet\n    error VoucherStillValid();\n    // Voucher has expired and cannot be transferred anymore\n    error VoucherHasExpired();\n    // Exchange has not been finalized yet\n    error ExchangeIsNotInAFinalState();\n    // Exchange with the same id already exists\n    error ExchangeAlreadyExists();\n    // Range length is 0, is more than quantity available or it would cause an overflow\n    error InvalidRangeLength();\n    // Exchange is being finalized into an invalid state\n    error InvalidTargeExchangeState();\n\n    // Twin related\n    // Twin does not exist\n    error NoSuchTwin();\n    // Seller did not approve the twin transfer\n    error NoTransferApproved();\n    // Twin transfer failed\n    error TwinTransferUnsuccessful();\n    // Token address is 0 or it does not implement the required interface\n    error UnsupportedToken();\n    // Twin cannot be removed if it's in a bundle\n    error BundleForTwinExists();\n    // Supply available is zero\n    error InvalidSupplyAvailable();\n    // Twin is Fungible or Multitoken and amount was set\n    error InvalidAmount();\n    // Twin is NonFungible and amount was not set\n    error InvalidTwinProperty(); // ToDo consider replacing with InvalidAmount\n    // Token range overlap with another, starting token id is too high or end of range would overflow\n    error InvalidTwinTokenRange();\n    // Token does not support IERC721 interface\n    error InvalidTokenAddress();\n\n    // Bundle related\n    // Bundle does not exist\n    error NoSuchBundle();\n    // Twin is not in a bundle\n    error TwinNotInBundle();\n    // Offer is not in a bundle\n    error OfferNotInBundle();\n    // Offer can appear in a bundle only once\n    error BundleOfferMustBeUnique();\n    // Twin can appear in a bundle only once\n    error BundleTwinMustBeUnique();\n    // Twin supply does not covver all offers in the bundle\n    error InsufficientTwinSupplyToCoverBundleOffers();\n    // Bundle cannot be created without an offer or a twin\n    error BundleRequiresAtLeastOneTwinAndOneOffer();\n\n    // Funds related\n    // Native token must be represented with zero address\n    error NativeWrongAddress();\n    // Amount sent along (msg.value) does not match the expected amount\n    error NativeWrongAmount();\n    // Token list lenght does not match the amount list length\n    error TokenAmountMismatch(); // ToDo consider replacing with ArrayLengthMismatch\n    // Token list is empty\n    error NothingToWithdraw();\n    // Call is not allowed to transfer the funds\n    error NotAuthorized();\n    // Token transfer failed\n    error TokenTransferFailed();\n    // Received amount does not match the expected amount\n    error InsufficientValueReceived();\n    // Seller's pool does not have enough funds to encumber\n    error InsufficientAvailableFunds();\n    // Native token was sent when ERC20 was expected\n    error NativeNotAllowed();\n    // Trying to deposit zero amount\n    error ZeroDepositNotAllowed();\n\n    // Meta-Transactions related\n    // Meta-transaction nonce is invalid\n    error NonceUsedAlready();\n    // Signature does not match the signer\n    error SignerAndSignatureDoNotMatch();\n    // Function signature does not match it's name\n    error InvalidFunctionName();\n    // Signature has invalid parameters\n    error InvalidSignature();\n    // Function is not allowed to be executed as a meta-transaction\n    error FunctionNotAllowlisted();\n\n    // Dispute related\n    // Dispute cannot be raised since the period to do it has elapsed\n    error DisputePeriodHasElapsed();\n    // Dispute cannot be resolved anymore and must be finalized with expireDispute\n    error DisputeHasExpired();\n    // Buyer gets more than 100% of the total pot\n    error InvalidBuyerPercent();\n    // Dispute is still valid and cannot be expired yet\n    error DisputeStillValid();\n    // New dispute timeout is earlier than existing dispute timeout\n    error InvalidDisputeTimeout();\n    // Absolute zero offers cannot be escalated\n    error EscalationNotAllowed();\n    // Dispute is being finalized into an invalid state\n    error InvalidTargeDisputeState();\n\n    // Config related\n    // Percentage exceeds 100%\n    error InvalidFeePercentage();\n    // Zero config value is not allowed\n    error ValueZeroNotAllowed();\n\n    // BosonVoucher\n    // Trying to issue an voucher that is in a reseverd range\n    error ExchangeIdInReservedRange();\n    // Trying to premint vouchers for an offer that does not have a reserved range\n    error NoReservedRangeForOffer();\n    // Trying to reserve a range that is already reserved\n    error OfferRangeAlreadyReserved();\n    // Range start at 0 is not allowed\n    error InvalidRangeStart();\n    // Amount to premint exceeds the range length\n    error InvalidAmountToMint();\n    // Trying to silent mint vouchers not belonging to the range owner\n    error NoSilentMintAllowed();\n    // Trying to premint the voucher of already expired offer\n    error OfferExpiredOrVoided();\n    // Trying to burn preminted vouchers of still valid offer\n    error OfferStillValid();\n    // Trying to burn more vouchers than available\n    error AmountExceedsRangeOrNothingToBurn();\n    // Royalty fee exceeds the max allowed\n    error InvalidRoyaltyFee();\n    // Trying to assign the premined vouchers to the address that is neither the contract owner nor the contract itself\n    error InvalidToAddress();\n    // Call to an external contract was not successful\n    error ExternalCallFailed();\n    // Trying to interact with external contract in a way that could result in transferring assets from the contract\n    error InteractionNotAllowed();\n\n    // Price discovery related\n    // Price discovery returned a price that does not match the expected one\n    error PriceMismatch();\n    // Token id is mandatory for bid orders and wrappers\n    error TokenIdMandatory();\n    // Incoming token id does not match the expected one\n    error TokenIdMismatch();\n    // Using price discovery for non-price discovery offer or using ordinary commit for price discovery offer\n    error InvalidPriceType();\n    // Missing price discovery contract address or data\n    error InvalidPriceDiscovery();\n    // Trying to set incoming voucher when it's already set, indicating reentrancy\n    error IncomingVoucherAlreadySet();\n    // Conduit address must be zero ()\n    error InvalidConduitAddress();\n    // Protocol does not know what token id to use\n    error TokenIdNotSet();\n    // Transferring a preminted voucher to wrong recipient\n    error VoucherTransferNotAllowed();\n    // Price discovery contract returned a negative price\n    error NegativePriceNotAllowed();\n    // Price discovery did not send the voucher to the protocol\n    error VoucherNotReceived();\n    // Price discovery did not send the voucher from the protocol\n    error VoucherNotTransferred();\n    // Either token with wrong id received or wrong voucher contract made the transfer\n    error UnexpectedERC721Received();\n    // Royalty fee exceeds the price\n    error FeeAmountTooHigh();\n    // Price does not cover the cancellation penalty\n    error PriceDoesNotCoverPenalty();\n}\n"
      },
      "contracts/domain/BosonTypes.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\n/**\n * @title BosonTypes\n *\n * @notice Enums and structs used by the Boson Protocol contract ecosystem.\n */\n\ncontract BosonTypes {\n    enum PausableRegion {\n        Offers,\n        Twins,\n        Bundles,\n        Groups,\n        Sellers,\n        Buyers,\n        DisputeResolvers,\n        Agents,\n        Exchanges,\n        Disputes,\n        Funds,\n        Orchestration,\n        MetaTransaction,\n        PriceDiscovery,\n        SequentialCommit\n    }\n\n    enum EvaluationMethod {\n        None, // None should always be at index 0. Never change this value.\n        Threshold,\n        SpecificToken\n    }\n\n    enum GatingType {\n        PerAddress,\n        PerTokenId\n    }\n\n    enum ExchangeState {\n        Committed,\n        Revoked,\n        Canceled,\n        Redeemed,\n        Completed,\n        Disputed\n    }\n\n    enum DisputeState {\n        Resolving,\n        Retracted,\n        Resolved,\n        Escalated,\n        Decided,\n        Refused\n    }\n\n    enum TokenType {\n        FungibleToken,\n        NonFungibleToken,\n        MultiToken\n    } // ERC20, ERC721, ERC1155\n\n    enum MetaTxInputType {\n        Generic,\n        CommitToOffer,\n        Exchange,\n        Funds,\n        CommitToConditionalOffer,\n        ResolveDispute\n    }\n\n    enum AuthTokenType {\n        None,\n        Custom, // For future use\n        Lens,\n        ENS\n    }\n\n    enum SellerUpdateFields {\n        Admin,\n        Assistant,\n        Clerk, // Deprecated.\n        AuthToken\n    }\n\n    enum DisputeResolverUpdateFields {\n        Admin,\n        Assistant,\n        Clerk // Deprecated.\n    }\n\n    enum PriceType {\n        Static, // Default should always be at index 0. Never change this value.\n        Discovery\n    }\n\n    struct AuthToken {\n        uint256 tokenId;\n        AuthTokenType tokenType;\n    }\n\n    struct Seller {\n        uint256 id;\n        address assistant;\n        address admin;\n        address clerk; // Deprecated. Kept for backwards compatibility.\n        address payable treasury;\n        bool active;\n        string metadataUri;\n    }\n\n    struct Buyer {\n        uint256 id;\n        address payable wallet;\n        bool active;\n    }\n\n    struct RoyaltyRecipient {\n        uint256 id;\n        address payable wallet;\n    }\n\n    struct DisputeResolver {\n        uint256 id;\n        uint256 escalationResponsePeriod;\n        address assistant;\n        address admin;\n        address clerk; // Deprecated. Kept for backwards compatibility.\n        address payable treasury;\n        string metadataUri;\n        bool active;\n    }\n\n    struct DisputeResolverFee {\n        address tokenAddress;\n        string tokenName;\n        uint256 feeAmount;\n    }\n\n    struct Agent {\n        uint256 id;\n        uint256 feePercentage;\n        address payable wallet;\n        bool active;\n    }\n\n    struct DisputeResolutionTerms {\n        uint256 disputeResolverId;\n        uint256 escalationResponsePeriod;\n        uint256 feeAmount;\n        uint256 buyerEscalationDeposit;\n    }\n\n    struct Offer {\n        uint256 id;\n        uint256 sellerId;\n        uint256 price;\n        uint256 sellerDeposit;\n        uint256 buyerCancelPenalty;\n        uint256 quantityAvailable;\n        address exchangeToken;\n        PriceType priceType;\n        string metadataUri;\n        string metadataHash;\n        bool voided;\n        uint256 collectionIndex;\n        RoyaltyInfo[] royaltyInfo;\n    }\n\n    struct OfferDates {\n        uint256 validFrom;\n        uint256 validUntil;\n        uint256 voucherRedeemableFrom;\n        uint256 voucherRedeemableUntil;\n    }\n\n    struct OfferDurations {\n        uint256 disputePeriod;\n        uint256 voucherValid;\n        uint256 resolutionPeriod;\n    }\n\n    struct Group {\n        uint256 id;\n        uint256 sellerId;\n        uint256[] offerIds;\n    }\n\n    struct Condition {\n        EvaluationMethod method;\n        TokenType tokenType;\n        address tokenAddress;\n        GatingType gating; // added in v2.3.0. All conditions created before that have a default value of \"PerAddress\"\n        uint256 minTokenId;\n        uint256 threshold;\n        uint256 maxCommits;\n        uint256 maxTokenId;\n    }\n\n    struct Exchange {\n        uint256 id;\n        uint256 offerId;\n        uint256 buyerId;\n        uint256 finalizedDate;\n        ExchangeState state;\n    }\n\n    struct ExchangeCosts {\n        uint256 resellerId;\n        uint256 price;\n        uint256 protocolFeeAmount;\n        uint256 royaltyAmount;\n        uint256 royaltyInfoIndex;\n    }\n\n    struct Voucher {\n        uint256 committedDate;\n        uint256 validUntilDate;\n        uint256 redeemedDate;\n        bool expired;\n    }\n\n    struct Dispute {\n        uint256 exchangeId;\n        uint256 buyerPercent;\n        DisputeState state;\n    }\n\n    struct DisputeDates {\n        uint256 disputed;\n        uint256 escalated;\n        uint256 finalized;\n        uint256 timeout;\n    }\n\n    struct Receipt {\n        uint256 exchangeId;\n        uint256 offerId;\n        uint256 buyerId;\n        uint256 sellerId;\n        uint256 price;\n        uint256 sellerDeposit;\n        uint256 buyerCancelPenalty;\n        OfferFees offerFees;\n        uint256 agentId;\n        address exchangeToken;\n        uint256 finalizedDate;\n        Condition condition;\n        uint256 committedDate;\n        uint256 redeemedDate;\n        bool voucherExpired;\n        uint256 disputeResolverId;\n        uint256 disputedDate;\n        uint256 escalatedDate;\n        DisputeState disputeState;\n        TwinReceipt[] twinReceipts;\n    }\n\n    struct TokenRange {\n        uint256 start;\n        uint256 end;\n        uint256 twinId;\n    }\n\n    struct Twin {\n        uint256 id;\n        uint256 sellerId;\n        uint256 amount; // ERC1155 / ERC20 (amount to be transferred to each buyer on redemption)\n        uint256 supplyAvailable; // all\n        uint256 tokenId; // ERC1155 / ERC721 (must be initialized with the initial pointer position of the ERC721 ids available range)\n        address tokenAddress; // all\n        TokenType tokenType;\n    }\n\n    struct TwinReceipt {\n        uint256 twinId;\n        uint256 tokenId; // only for ERC721 and ERC1155\n        uint256 amount; // only for ERC1155 and ERC20\n        address tokenAddress;\n        TokenType tokenType;\n    }\n\n    struct Bundle {\n        uint256 id;\n        uint256 sellerId;\n        uint256[] offerIds;\n        uint256[] twinIds;\n    }\n\n    struct Funds {\n        address tokenAddress;\n        string tokenName;\n        uint256 availableAmount;\n    }\n\n    struct MetaTransaction {\n        uint256 nonce;\n        address from;\n        address contractAddress;\n        string functionName;\n        bytes functionSignature;\n    }\n\n    struct HashInfo {\n        bytes32 typeHash;\n        function(bytes memory) internal pure returns (bytes32) hashFunction;\n    }\n\n    struct OfferFees {\n        uint256 protocolFee;\n        uint256 agentFee;\n    }\n\n    struct VoucherInitValues {\n        string contractURI;\n        uint256 royaltyPercentage;\n        bytes32 collectionSalt;\n    }\n\n    struct Collection {\n        address collectionAddress;\n        string externalId;\n    }\n\n    struct PriceDiscovery {\n        uint256 price;\n        Side side;\n        address priceDiscoveryContract;\n        address conduit;\n        bytes priceDiscoveryData;\n    }\n\n    enum Side {\n        Ask,\n        Bid,\n        Wrapper // Side is not relevant from the protocol perspective\n    }\n\n    struct RoyaltyInfo {\n        address payable[] recipients;\n        uint256[] bps;\n    }\n\n    struct RoyaltyRecipientInfo {\n        address payable wallet;\n        uint256 minRoyaltyPercentage;\n    }\n\n    struct PremintParameters {\n        uint256 reservedRangeLength;\n        address to;\n    }\n\n    struct Payoff {\n        uint256 seller;\n        uint256 buyer;\n        uint256 protocol;\n        uint256 agent;\n    }\n}\n"
      },
      "contracts/interfaces/clients/IBosonVoucher.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport { IERC721Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol\";\nimport { IERC721MetadataUpgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol\";\nimport { IERC721ReceiverUpgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol\";\n\n/**\n * @title IBosonVoucher\n *\n * @notice This is the interface for the Boson Protocol ERC-721 Voucher contract.\n *\n * The ERC-165 identifier for this interface is: 0x6a474d2c\n */\ninterface IBosonVoucher is IERC721Upgradeable, IERC721MetadataUpgradeable, IERC721ReceiverUpgradeable {\n    event ContractURIChanged(string contractURI);\n    event VoucherInitialized(uint256 indexed sellerId, string indexed contractURI);\n    event RangeReserved(uint256 indexed offerId, Range range);\n    event VouchersPreMinted(uint256 indexed offerId, uint256 startId, uint256 endId);\n\n    // Describe a reserved range of token ids\n    struct Range {\n        uint256 start; // First token id of range\n        uint256 length; // Length of range\n        uint256 minted; // Amount pre-minted so far\n        uint256 lastBurnedTokenId; // Last burned token id\n        address owner; // The range owner\n    }\n\n    /**\n     * @notice Issues a voucher to a buyer.\n     *\n     * Minted voucher supply is sent to the buyer.\n     * Caller must have PROTOCOL role.\n     *\n     * @param _tokenId - voucher token id corresponds to <<uint128(offerId)>>.<<uint128(exchangeId)>>\n     * @param _buyer - the buyer address\n     */\n    function issueVoucher(uint256 _tokenId, address _buyer) external;\n\n    /**\n     * @notice Burns a voucher.\n     *\n     * Caller must have PROTOCOL role.\n     *\n     * @param _tokenId - voucher token id corresponds to <<uint128(offerId)>>.<<uint128(exchangeId)>>\n     */\n    function burnVoucher(uint256 _tokenId) external;\n\n    /**\n     * @notice Gets the seller id.\n     *\n     * @return the id for the Voucher seller\n     */\n    function getSellerId() external view returns (uint256);\n\n    /**\n     * @notice Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the protocol. Change is done by calling `updateSeller` on the protocol.\n     *\n     * @param newOwner - the address to which ownership of the voucher contract will be transferred\n     */\n    function transferOwnership(address newOwner) external;\n\n    /**\n     * @notice Returns storefront-level metadata used by OpenSea.\n     *\n     * @return Contract metadata URI\n     */\n    function contractURI() external view returns (string memory);\n\n    /**\n     * @notice Sets new contract URI.\n     * Can only be called by the owner or during the initialization.\n     *\n     * @param _newContractURI - new contract metadata URI\n     */\n    function setContractURI(string calldata _newContractURI) external;\n\n    /**\n     * @notice Provides royalty info.\n     * Called with the sale price to determine how much royalty is owed and to whom.\n     *\n     * @param _tokenId - the voucher queried for royalty information\n     * @param _salePrice - the sale price of the voucher specified by _tokenId\n     *\n     * @return receiver - address of who should be sent the royalty payment\n     * @return royaltyAmount - the royalty payment amount for the given sale price\n     */\n    function royaltyInfo(\n        uint256 _tokenId,\n        uint256 _salePrice\n    ) external view returns (address receiver, uint256 royaltyAmount);\n\n    /**\n     * @notice Reserves a range of vouchers to be associated with an offer\n     *\n     * Must happen prior to calling preMint\n     * Caller must have PROTOCOL role.\n     *\n     * Reverts if:\n     * - Start id is not greater than zero for the first range\n     * - Start id is not greater than the end id of the previous range for subsequent ranges\n     * - Range length is zero\n     * - Range length is too large, i.e., would cause an overflow\n     * - Offer id is already associated with a range\n     * - _to is not the contract address or the contract owner\n     *\n     * @param _offerId - the id of the offer\n     * @param _start - the first id of the token range\n     * @param _length - the length of the range\n     * @param _to - the address to send the pre-minted vouchers to (contract address or contract owner)\n     */\n    function reserveRange(uint256 _offerId, uint256 _start, uint256 _length, address _to) external;\n\n    /**\n     * @notice Pre-mints all or part of an offer's reserved vouchers.\n     *\n     * For small offer quantities, this method may only need to be\n     * called once.\n     *\n     * But, if the range is large, e.g., 10k vouchers, block gas limit\n     * could cause the transaction to fail. Thus, in order to support\n     * a batched approach to pre-minting an offer's vouchers,\n     * this method can be called multiple times, until the whole\n     * range is minted.\n     *\n     * A benefit to the batched approach is that the entire reserved\n     * range for an offer need not be pre-minted at one time. A seller\n     * could just mint batches periodically, controlling the amount\n     * that are available on the market at any given time, e.g.,\n     * creating a pre-minted offer with a validity period of one year,\n     * causing the token range to be reserved, but only pre-minting\n     * a certain amount monthly.\n     *\n     * Caller must be contract owner (seller assistant address).\n     *\n     * Reverts if:\n     * - Offer id is not associated with a range\n     * - Amount to mint is more than remaining un-minted in range\n     * - Too many to mint in a single transaction, given current block gas limit\n     *\n     * @param _offerId - the id of the offer\n     * @param _amount - the amount to mint\n     */\n    function preMint(uint256 _offerId, uint256 _amount) external;\n\n    /**\n     * @notice Burn all or part of an offer's preminted vouchers.\n     * If offer expires or it's voided, the seller can burn the preminted vouchers that were not transferred yet.\n     * This way they will not show in seller's wallet and marketplaces anymore.\n     *\n     * For small offer quantities, this method may only need to be\n     * called once.\n     *\n     * But, if the range is large, e.g., 10k vouchers, block gas limit\n     * could cause the transaction to fail. Thus, in order to support\n     * a batched approach to pre-minting an offer's vouchers,\n     * this method can be called multiple times, until the whole\n     * range is burned.\n     *\n     * Caller must be contract owner (seller assistant address).\n     *\n     * Reverts if:\n     * - Offer id is not associated with a range\n     * - Offer is not expired or voided\n     * - There is nothing to burn\n     *\n     * @param _offerId - the id of the offer\n     * @param _amount - amount to burn\n     */\n    function burnPremintedVouchers(uint256 _offerId, uint256 _amount) external;\n\n    /**\n     * @notice Gets the number of vouchers available to be pre-minted for an offer.\n     *\n     * @param _offerId - the id of the offer\n     * @return count - the count of vouchers in reserved range available to be pre-minted\n     */\n    function getAvailablePreMints(uint256 _offerId) external view returns (uint256 count);\n\n    /**\n     * @notice Gets the range for an offer.\n     *\n     * @param _offerId - the id of the offer\n     * @return range - range struct with information about range start, length and already minted tokens\n     */\n    function getRangeByOfferId(uint256 _offerId) external view returns (Range memory range);\n\n    /**\n     * @notice Make a call to an external contract.\n     *\n     * Reverts if:\n     * - _to is zero address\n     * - call to external contract fails\n     * - caller is not the owner\n     * - _to is a contract that represents some assets (all contracts that implement `balanceOf` method, including ERC20 and ERC721)\n     *\n     * @param _to - address of the contract to call\n     * @param _data - data to pass to the external contract\n     * @return result - result of the call\n     */\n    function callExternalContract(address _to, bytes memory _data) external payable returns (bytes memory);\n\n    /** @notice Set approval for all to the vouchers owned by this contract\n     *\n     * Reverts if:\n     * - _operator is zero address\n     * - caller is not the owner\n     * - _operator is this contract\n     *\n     * @param _operator - address of the operator to set approval for\n     * @param _approved - true to approve the operator in question, false to revoke approval\n     */\n    function setApprovalForAllToContract(address _operator, bool _approved) external;\n\n    /**\n     * @notice Withdraw funds from the contract to the protocol seller pool\n     *\n     * @param _tokenList - list of tokens to withdraw, including native token (address(0))\n     */\n    function withdrawToProtocol(address[] calldata _tokenList) external;\n}\n"
      },
      "contracts/interfaces/diamond/IDiamondCut.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\n/**\n * @title IDiamondCut\n *\n * @notice Manages Diamond Facets.\n *\n * Reference Implementation  : https://github.com/mudgen/diamond-2-hardhat\n * EIP-2535 Diamond Standard : https://eips.ethereum.org/EIPS/eip-2535\n *\n * The ERC-165 identifier for this interface is: 0x1f931c1c\n *\n * @author Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)\n */\ninterface IDiamondCut {\n    event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);\n\n    enum FacetCutAction {\n        Add,\n        Replace,\n        Remove\n    }\n\n    struct FacetCut {\n        address facetAddress;\n        FacetCutAction action;\n        bytes4[] functionSelectors;\n    }\n\n    /**\n     * @notice Cuts facets of the Diamond.\n     *\n     * Adds/replaces/removes any number of function selectors.\n     *\n     * If populated, _calldata is executed with delegatecall on _init\n     *\n     * Reverts if caller does not have UPGRADER role\n     *\n     * @param _facetCuts - contains the facet addresses and function selectors\n     * @param _init - the address of the contract or facet to execute _calldata\n     * @param _calldata - a function call, including function selector and arguments\n     */\n    function diamondCut(FacetCut[] calldata _facetCuts, address _init, bytes calldata _calldata) external;\n}\n"
      },
      "contracts/interfaces/events/IBosonAccountEvents.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\n\n/**\n * @title IBosonAccountEvents\n *\n * @notice Defines events related to management of accounts within the protocol.\n */\ninterface IBosonAccountEvents {\n    event SellerCreated(\n        uint256 indexed sellerId,\n        BosonTypes.Seller seller,\n        address voucherCloneAddress,\n        BosonTypes.AuthToken authToken,\n        address indexed executedBy\n    );\n    event SellerUpdatePending(\n        uint256 indexed sellerId,\n        BosonTypes.Seller pendingSeller,\n        BosonTypes.AuthToken pendingAuthToken,\n        address indexed executedBy\n    );\n    event SellerUpdateApplied(\n        uint256 indexed sellerId,\n        BosonTypes.Seller seller,\n        BosonTypes.Seller pendingSeller,\n        BosonTypes.AuthToken authToken,\n        BosonTypes.AuthToken pendingAuthToken,\n        address indexed executedBy\n    );\n    event RoyaltyRecipientsChanged(\n        uint256 indexed sellerId,\n        BosonTypes.RoyaltyRecipientInfo[] royaltyRecipients,\n        address indexed executedBy\n    );\n    event BuyerCreated(uint256 indexed buyerId, BosonTypes.Buyer buyer, address indexed executedBy);\n    event BuyerUpdated(uint256 indexed buyerId, BosonTypes.Buyer buyer, address indexed executedBy);\n    event AgentUpdated(uint256 indexed agentId, BosonTypes.Agent agent, address indexed executedBy);\n    event DisputeResolverCreated(\n        uint256 indexed disputeResolverId,\n        BosonTypes.DisputeResolver disputeResolver,\n        BosonTypes.DisputeResolverFee[] disputeResolverFees,\n        uint256[] sellerAllowList,\n        address indexed executedBy\n    );\n    event DisputeResolverUpdatePending(\n        uint256 indexed disputeResolverId,\n        BosonTypes.DisputeResolver pendingDisputeResolver,\n        address indexed executedBy\n    );\n    event DisputeResolverUpdateApplied(\n        uint256 indexed disputeResolverId,\n        BosonTypes.DisputeResolver disputeResolver,\n        BosonTypes.DisputeResolver pendingDisputeResolver,\n        address indexed executedBy\n    );\n    event DisputeResolverFeesAdded(\n        uint256 indexed disputeResolverId,\n        BosonTypes.DisputeResolverFee[] disputeResolverFees,\n        address indexed executedBy\n    );\n    event DisputeResolverFeesRemoved(\n        uint256 indexed disputeResolverId,\n        address[] feeTokensRemoved,\n        address indexed executedBy\n    );\n    event AllowedSellersAdded(uint256 indexed disputeResolverId, uint256[] addedSellers, address indexed executedBy);\n    event AllowedSellersRemoved(\n        uint256 indexed disputeResolverId,\n        uint256[] removedSellers,\n        address indexed executedBy\n    );\n    event AgentCreated(uint256 indexed agentId, BosonTypes.Agent agent, address indexed executedBy);\n    event CollectionCreated(\n        uint256 indexed sellerId,\n        uint256 collectionIndex,\n        address collectionAddress,\n        string indexed externalId,\n        address indexed executedBy\n    );\n}\n"
      },
      "contracts/interfaces/handlers/IBosonSellerHandler.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\n\n/**\n * @title IBosonSellerHandler\n *\n * @notice Handles creation, update, retrieval of sellers within the protocol.\n *\n * The ERC-165 identifier for this interface is: 0x3e8eaeef\n */\ninterface IBosonSellerHandler {\n    /**\n     * @notice Creates a seller.\n     *\n     * Emits a SellerCreated event if successful.\n     *\n     * Reverts if:\n     * - Caller is not the supplied admin or does not own supplied auth token\n     * - Caller is not the supplied assistant\n     * - Supplied clerk is not a zero address\n     * - The sellers region of protocol is paused\n     * - Address values are zero address\n     * - Addresses are not unique to this seller\n     * - Seller is not active (if active == false)\n     * - Admin address is zero address and AuthTokenType == None\n     * - AuthTokenType is not unique to this seller\n     * - AuthTokenType is Custom\n     * - Seller salt is not unique\n     * - Clone creation fails\n     *\n     * @param _seller - the fully populated struct with seller id set to 0x0\n     * @param _authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     * @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct\n     */\n    function createSeller(\n        BosonTypes.Seller memory _seller,\n        BosonTypes.AuthToken calldata _authToken,\n        BosonTypes.VoucherInitValues calldata _voucherInitValues\n    ) external;\n\n    /**\n     * @notice Updates treasury address, if changed. Puts admin, assistant and AuthToken in pending queue, if changed.\n     *         Pending updates can be completed by calling the optInToSellerUpdate function.\n     * @dev    Active flag passed in by caller will be ignored. The value from storage will be used.\n     *\n     * Emits a SellerUpdateApplied event if the seller has changed the treasury.\n     * Emits a SellerUpdatePending event if the seller has requested an update for admin, assistant, or auth token.\n     * Holder of new auth token and/or owner(s) of new addresses for admin, assistant must opt-in to the update.\n     *\n     * Reverts if:\n     * - The sellers region of protocol is paused\n     * - Address values are zero address\n     * - Addresses are not unique to this seller\n     * - Supplied clerk is not a zero address\n     * - Caller address is not the admin address of the stored seller with no AuthToken\n     * - Caller is not the owner of the seller's stored AuthToken\n     * - Seller does not exist\n     * - Admin address is zero address and AuthTokenType == None\n     * - AuthTokenType is not unique to this seller\n     * - AuthTokenType is Custom\n     * - No field has been updated or requested to be updated\n     *\n     * @param _seller - the fully populated seller struct\n     * @param _authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     */\n    function updateSeller(BosonTypes.Seller memory _seller, BosonTypes.AuthToken calldata _authToken) external;\n\n    /**\n     * @notice Opt-in to a pending seller update\n     *\n     * Emits a SellerUpdateApplied event if successful.\n     *\n     * Reverts if:\n     * - The sellers region of protocol is paused\n     * - Addresses are not unique to this seller\n     * - Caller address is not pending update for the field being updated\n     * - Caller is not the owner of the pending AuthToken being updated\n     * - No pending update exists for this seller\n     * - AuthTokenType is not unique to this seller\n     * - Seller tries to update the clerk\n     *\n     * @param _sellerId - seller id\n     * @param _fieldsToUpdate - fields to update, see SellerUpdateFields enum\n     */\n    function optInToSellerUpdate(uint256 _sellerId, BosonTypes.SellerUpdateFields[] calldata _fieldsToUpdate) external;\n\n    /**\n     * @notice Adds royalty recipients to a seller.\n     *\n     * Emits a RoyalRecipientsUpdated event if successful.\n     *\n     *  Reverts if:\n     *  - The sellers region of protocol is paused\n     *  - Seller does not exist\n     *  - Caller is not the seller admin\n     *  - Caller does not own auth token\n     *  - Some recipient is not unique\n     *  - some royalty percentage is above the limit\n     *\n     * @param _sellerId - seller id\n     * @param _royaltyRecipients - list of royalty recipients to add\n     */\n    function addRoyaltyRecipients(\n        uint256 _sellerId,\n        BosonTypes.RoyaltyRecipientInfo[] calldata _royaltyRecipients\n    ) external;\n\n    /**\n     * @notice Updates seller's royalty recipients.\n     *\n     * Emits a RoyalRecipientsUpdated event if successful.\n     *\n     *  Reverts if:\n     *  - The sellers region of protocol is paused\n     *  - Seller does not exist\n     *  - Caller is not the seller admin\n     *  - Caller does not own auth token\n     *  - Length of ids to change does not match length of new values\n     *  - Id to update does not exist\n     *  - Seller tries to update the address of default recipient\n     *  - Some recipient is not unique\n     *  - Some royalty percentage is above the limit\n     *\n     * @param _sellerId - seller id\n     * @param _royaltyRecipientIds - list of royalty recipient ids to update. Ids are zero based and corresponds to ids returned by `getRoyaltyRecipients`.\n     * @param _royaltyRecipients - list of new royalty recipients corresponding to ids\n     */\n    function updateRoyaltyRecipients(\n        uint256 _sellerId,\n        uint256[] calldata _royaltyRecipientIds,\n        BosonTypes.RoyaltyRecipientInfo[] calldata _royaltyRecipients\n    ) external;\n\n    /**\n     * @notice Removes seller's royalty recipients.\n     *\n     * Emits a RoyalRecipientsUpdated event if successful.\n     *\n     *  Reverts if:\n     *  - The sellers region of protocol is paused\n     *  - Seller does not exist\n     *  - Caller is not the seller admin\n     *  - Caller does not own auth token\n     *  - List of ids to remove is not sorted in ascending order\n     *  - Id to remove does not exist\n     *  - Seller tries to remove the default recipient\n     *\n     * @param _sellerId - seller id\n     * @param _royaltyRecipientIds - list of royalty recipient ids to remove. Ids are zero based and corresponds to ids returned by `getRoyaltyRecipients`.\n     */\n    function removeRoyaltyRecipients(uint256 _sellerId, uint256[] calldata _royaltyRecipientIds) external;\n\n    /**\n     * @notice Creates a new seller collection.\n     *\n     * Emits a CollectionCreated event if successful.\n     *\n     * Reverts if:\n     * - The sellers region of protocol is paused\n     * - Caller is not the seller assistant\n     *\n     * @param _externalId - external collection id\n     * @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct\n     */\n    function createNewCollection(\n        string calldata _externalId,\n        BosonTypes.VoucherInitValues calldata _voucherInitValues\n    ) external;\n\n    /**\n     * @notice Updates a salt.\n     * Use this if the admin address is updated and there exists a possibility that the old admin will try to create the vouchers\n     * with matching addresses on other chains.\n     *\n     * Reverts if:\n     * - The sellers region of protocol is paused\n     * - Caller is not the admin of any seller\n     * - Seller salt is not unique\n     *\n     * @param _sellerId - the id of the seller\n     * @param _newSalt - new salt\n     */\n    function updateSellerSalt(uint256 _sellerId, bytes32 _newSalt) external;\n\n    /**\n     * @notice Gets the details about a seller.\n     *\n     * @param _sellerId - the id of the seller to check\n     * @return exists - the seller was found\n     * @return seller - the seller details. See {BosonTypes.Seller}\n     * @return authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     *                     See {BosonTypes.AuthToken}\n     */\n    function getSeller(\n        uint256 _sellerId\n    ) external view returns (bool exists, BosonTypes.Seller memory seller, BosonTypes.AuthToken memory authToken);\n\n    /**\n     * @notice Gets the details about a seller by an address associated with that seller: assistant, or admin address.\n     * A seller will have either an admin address or an auth token.\n     * If seller's admin uses NFT Auth the seller should call `getSellerByAuthToken` instead.\n     *\n     * @param _associatedAddress - the address associated with the seller. Must be an assistant, or admin  address.\n     * @return exists - the seller was found\n     * @return seller - the seller details. See {BosonTypes.Seller}\n     * @return authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     *                     See {BosonTypes.AuthToken}\n     */\n    function getSellerByAddress(\n        address _associatedAddress\n    ) external view returns (bool exists, BosonTypes.Seller memory seller, BosonTypes.AuthToken memory authToken);\n\n    /**\n     * @notice Gets the details about a seller by an auth token associated with that seller.\n     * A seller will have either an admin address or an auth token.\n     * If seller's admin uses an admin address, the seller should call `getSellerByAddress` instead.\n     *\n     *\n     * @param _associatedAuthToken - the auth token that may be associated with the seller.\n     * @return exists - the seller was found\n     * @return seller - the seller details. See {BosonTypes.Seller}\n     * @return authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     *                     See {BosonTypes.AuthToken}\n     */\n    function getSellerByAuthToken(\n        BosonTypes.AuthToken calldata _associatedAuthToken\n    ) external view returns (bool exists, BosonTypes.Seller memory seller, BosonTypes.AuthToken memory authToken);\n\n    /**\n     * @notice Gets the details about all seller's collections.\n     * In case seller has too many collections and this runs out of gas, please use getSellersCollectionsPaginated.\n     *\n     * @param _sellerId - the id of the seller to check\n     * @return defaultVoucherAddress - the address of the default voucher contract for the seller\n     * @return additionalCollections - an array of additional collections that the seller has created\n     */\n    function getSellersCollections(\n        uint256 _sellerId\n    ) external view returns (address defaultVoucherAddress, BosonTypes.Collection[] memory additionalCollections);\n\n    /**\n     * @notice Gets the details about all seller's collections.\n     * Use getSellersCollectionCount to get the total number of collections.\n     *\n     * @param _sellerId - the id of the seller to check\n     * @param _limit - the maximum number of Collections that should be returned starting from the index defined by `_offset`. If `_offset` + `_limit` exceeds total number of collections, `_limit` is adjusted to return all remaining collections.\n     * @param _offset - the starting index from which to return collections. If `_offset` is greater than or equal to total number of collections, an empty list is returned.\n     * @return defaultVoucherAddress - the address of the default voucher contract for the seller\n     * @return additionalCollections - an array of additional collections that the seller has created\n     */\n    function getSellersCollectionsPaginated(\n        uint256 _sellerId,\n        uint256 _limit,\n        uint256 _offset\n    ) external view returns (address defaultVoucherAddress, BosonTypes.Collection[] memory additionalCollections);\n\n    /**\n     * @notice Returns the number of additional collections for a seller.\n     * Use this in conjunction with getSellersCollectionsPaginated to get all collections.\n     *\n     * @param _sellerId - the id of the seller to check\n     */\n    function getSellersCollectionCount(uint256 _sellerId) external view returns (uint256 collectionCount);\n\n    /**\n     * @notice Returns the availability of salt for a seller.\n     *\n     * @param _adminAddres - the admin address to check\n     * @param _salt - the salt to check (corresponds to `collectionSalt` when `createSeler` or `createNewCollection` is called or `newSalt` when `updateSellerSalt` is called)\n     * @return isAvailable - salt can be used\n     */\n    function isSellerSaltAvailable(address _adminAddres, bytes32 _salt) external view returns (bool isAvailable);\n\n    /**\n     * @notice Calculates the expected collection address and tells if it's still avaialble.\n     *\n     * @param _sellerId - the seller id\n     * @param _collectionSalt - the collection specific salt\n     * @return collectionAddress - the collection address\n     * @return isAvailable - whether the collection address is available\n     */\n    function calculateCollectionAddress(\n        uint256 _sellerId,\n        bytes32 _collectionSalt\n    ) external view returns (address collectionAddress, bool isAvailable);\n\n    /**\n     * @notice Gets seller's royalty recipients.\n     *\n     * @param _sellerId - seller id\n     * @return royaltyRecipients - list of royalty recipients\n     */\n    function getRoyaltyRecipients(\n        uint256 _sellerId\n    ) external view returns (BosonTypes.RoyaltyRecipientInfo[] memory royaltyRecipients);\n}\n"
      },
      "contracts/interfaces/IAccessControl.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity 0.8.22;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     *\n     * _Available since v3.1._\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     */\n    function renounceRole(bytes32 role, address account) external;\n}\n"
      },
      "contracts/interfaces/IERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity 0.8.22;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
      },
      "contracts/interfaces/IERC721.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)\n\npragma solidity 0.8.22;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n    /**\n     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n     */\n    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n     */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n     * @dev Returns the number of tokens in ``owner``'s account.\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Transfers `tokenId` token from `from` to `to`.\n     *\n     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n     * The approval is cleared when the token is transferred.\n     *\n     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n     *\n     * Requirements:\n     *\n     * - The caller must own the token or be an approved operator.\n     * - `tokenId` must exist.\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address to, uint256 tokenId) external;\n\n    /**\n     * @dev Returns the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\n\n    /**\n     * @dev Approve or remove `operator` as an operator for the caller.\n     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n     *\n     * Requirements:\n     *\n     * - The `operator` cannot be the caller.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function setApprovalForAll(address operator, bool _approved) external;\n\n    /**\n     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n     *\n     * See {setApprovalForAll}\n     */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n}\n"
      },
      "contracts/interfaces/IInitializableVoucherClone.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport \"../domain/BosonTypes.sol\";\n\ninterface IInitializableVoucherClone {\n    /**\n     * @notice Initializes the contract with the address of the beacon contract.\n     *\n     * @param _beaconAddress - Address of the beacon contract.\n     */\n    function initialize(address _beaconAddress) external;\n\n    /**\n     * @notice Initializes a voucher with the given parameters.\n     *\n     * @param _sellerId - The ID of the seller.\n     * @param _collectionIndex - The index of the collection.\n     * @param _newOwner - The address of the new owner.\n     * @param _voucherInitValues - The voucher initialization values.\n     */\n    function initializeVoucher(\n        uint256 _sellerId,\n        uint256 _collectionIndex,\n        address _newOwner,\n        BosonTypes.VoucherInitValues calldata _voucherInitValues\n    ) external;\n}\n"
      },
      "contracts/protocol/bases/PausableBase.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport \"./../../domain/BosonConstants.sol\";\nimport { BosonErrors } from \"../../domain/BosonErrors.sol\";\nimport { ProtocolLib } from \"../libs/ProtocolLib.sol\";\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\n\n/**\n * @title PausableBase\n *\n * @notice Provides modifiers for regional pausing\n */\ncontract PausableBase is BosonTypes {\n    /**\n     * @notice Modifier that checks the Offers region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier offersNotPaused() {\n        revertIfPaused(PausableRegion.Offers);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Twins region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier twinsNotPaused() {\n        revertIfPaused(PausableRegion.Twins);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Bundles region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier bundlesNotPaused() {\n        revertIfPaused(PausableRegion.Bundles);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Groups region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier groupsNotPaused() {\n        revertIfPaused(PausableRegion.Groups);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Sellers region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier sellersNotPaused() {\n        revertIfPaused(PausableRegion.Sellers);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Buyers region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier buyersNotPaused() {\n        revertIfPaused(PausableRegion.Buyers);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Agents region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier agentsNotPaused() {\n        revertIfPaused(PausableRegion.Agents);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the DisputeResolvers region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier disputeResolversNotPaused() {\n        revertIfPaused(PausableRegion.DisputeResolvers);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Exchanges region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier exchangesNotPaused() {\n        revertIfPaused(PausableRegion.Exchanges);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Disputes region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier disputesNotPaused() {\n        revertIfPaused(PausableRegion.Disputes);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Funds region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier fundsNotPaused() {\n        revertIfPaused(PausableRegion.Funds);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the Orchestration region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier orchestrationNotPaused() {\n        revertIfPaused(PausableRegion.Orchestration);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the MetaTransaction region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier metaTransactionsNotPaused() {\n        revertIfPaused(PausableRegion.MetaTransaction);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the PriceDiscovery region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier priceDiscoveryNotPaused() {\n        revertIfPaused(PausableRegion.PriceDiscovery);\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks the SequentialCommit region is not paused\n     *\n     * Reverts if region is paused\n     *\n     * See: {BosonTypes.PausableRegion}\n     */\n    modifier sequentialCommitNotPaused() {\n        revertIfPaused(PausableRegion.SequentialCommit);\n        _;\n    }\n\n    /**\n     * @notice Checks if a region of the protocol is paused.\n     *\n     * Reverts if region is paused\n     *\n     * @param _region the region to check pause status for\n     */\n    function revertIfPaused(PausableRegion _region) internal view {\n        // Region enum value must be used as the exponent in a power of 2\n        uint256 powerOfTwo = 1 << uint256(_region);\n        if ((ProtocolLib.protocolStatus().pauseScenario & powerOfTwo) == powerOfTwo)\n            revert BosonErrors.RegionPaused(_region);\n    }\n}\n"
      },
      "contracts/protocol/bases/ProtocolBase.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport \"../../domain/BosonConstants.sol\";\nimport { BosonErrors } from \"../../domain/BosonErrors.sol\";\nimport { ProtocolLib } from \"../libs/ProtocolLib.sol\";\nimport { DiamondLib } from \"../../diamond/DiamondLib.sol\";\nimport { EIP712Lib } from \"../libs/EIP712Lib.sol\";\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\nimport { PausableBase } from \"./PausableBase.sol\";\nimport { ReentrancyGuardBase } from \"./ReentrancyGuardBase.sol\";\n\n/**\n * @title ProtocolBase\n *\n * @notice Provides domain and common modifiers to Protocol facets\n */\nabstract contract ProtocolBase is PausableBase, ReentrancyGuardBase, BosonErrors {\n    /**\n     * @notice Modifier to protect initializer function from being invoked twice.\n     */\n    modifier onlyUninitialized(bytes4 interfaceId) {\n        ProtocolLib.ProtocolStatus storage ps = protocolStatus();\n        if (ps.initializedInterfaces[interfaceId]) revert AlreadyInitialized();\n        ps.initializedInterfaces[interfaceId] = true;\n        _;\n    }\n\n    /**\n     * @notice Modifier that checks that the caller has a specific role.\n     *\n     * Reverts if caller doesn't have role.\n     *\n     * See: {AccessController.hasRole}\n     *\n     * @param _role - the role to check\n     */\n    modifier onlyRole(bytes32 _role) {\n        DiamondLib.DiamondStorage storage ds = DiamondLib.diamondStorage();\n        if (!ds.accessController.hasRole(_role, msgSender())) revert AccessDenied();\n        _;\n    }\n\n    /**\n     * @notice Get the Protocol Addresses slot\n     *\n     * @return pa - the Protocol Addresses slot\n     */\n    function protocolAddresses() internal pure returns (ProtocolLib.ProtocolAddresses storage pa) {\n        pa = ProtocolLib.protocolAddresses();\n    }\n\n    /**\n     * @notice Get the Protocol Limits slot\n     *\n     * @return pl - the Protocol Limits slot\n     */\n    function protocolLimits() internal pure returns (ProtocolLib.ProtocolLimits storage pl) {\n        pl = ProtocolLib.protocolLimits();\n    }\n\n    /**\n     * @notice Get the Protocol Entities slot\n     *\n     * @return pe - the Protocol Entities slot\n     */\n    function protocolEntities() internal pure returns (ProtocolLib.ProtocolEntities storage pe) {\n        pe = ProtocolLib.protocolEntities();\n    }\n\n    /**\n     * @notice Get the Protocol Lookups slot\n     *\n     * @return pl - the Protocol Lookups slot\n     */\n    function protocolLookups() internal pure returns (ProtocolLib.ProtocolLookups storage pl) {\n        pl = ProtocolLib.protocolLookups();\n    }\n\n    /**\n     * @notice Get the Protocol Fees slot\n     *\n     * @return pf - the Protocol Fees slot\n     */\n    function protocolFees() internal pure returns (ProtocolLib.ProtocolFees storage pf) {\n        pf = ProtocolLib.protocolFees();\n    }\n\n    /**\n     * @notice Get the Protocol Counters slot\n     *\n     * @return pc the Protocol Counters slot\n     */\n    function protocolCounters() internal pure returns (ProtocolLib.ProtocolCounters storage pc) {\n        pc = ProtocolLib.protocolCounters();\n    }\n\n    /**\n     * @notice Get the Protocol meta-transactions storage slot\n     *\n     * @return pmti the Protocol meta-transactions storage slot\n     */\n    function protocolMetaTxInfo() internal pure returns (ProtocolLib.ProtocolMetaTxInfo storage pmti) {\n        pmti = ProtocolLib.protocolMetaTxInfo();\n    }\n\n    /**\n     * @notice Get the Protocol Status slot\n     *\n     * @return ps the Protocol Status slot\n     */\n    function protocolStatus() internal pure returns (ProtocolLib.ProtocolStatus storage ps) {\n        ps = ProtocolLib.protocolStatus();\n    }\n\n    /**\n     * @notice Gets a seller id from storage by assistant address\n     *\n     * @param _assistant - the assistant address of the seller\n     * @return exists - whether the seller id exists\n     * @return sellerId  - the seller id\n     */\n    function getSellerIdByAssistant(address _assistant) internal view returns (bool exists, uint256 sellerId) {\n        // Get the seller id\n        sellerId = protocolLookups().sellerIdByAssistant[_assistant];\n\n        // Determine existence\n        exists = (sellerId > 0);\n    }\n\n    /**\n     * @notice Gets a seller id from storage by admin address\n     *\n     * @param _admin - the admin address of the seller\n     * @return exists - whether the seller id exists\n     * @return sellerId  - the seller id\n     */\n    function getSellerIdByAdmin(address _admin) internal view returns (bool exists, uint256 sellerId) {\n        // Get the seller id\n        sellerId = protocolLookups().sellerIdByAdmin[_admin];\n\n        // Determine existence\n        exists = (sellerId > 0);\n    }\n\n    /**\n     * @notice Gets a seller id from storage by auth token.  A seller will have either an admin address or an auth token\n     *\n     * @param _authToken - the potential _authToken of the seller.\n     * @return exists - whether the seller id exists\n     * @return sellerId  - the seller id\n     */\n    function getSellerIdByAuthToken(\n        AuthToken calldata _authToken\n    ) internal view returns (bool exists, uint256 sellerId) {\n        // Get the seller id\n        sellerId = protocolLookups().sellerIdByAuthToken[_authToken.tokenType][_authToken.tokenId];\n\n        // Determine existence\n        exists = (sellerId > 0);\n    }\n\n    /**\n     * @notice Gets a buyer id from storage by wallet address\n     *\n     * @param _wallet - the wallet address of the buyer\n     * @return exists - whether the buyer id exists\n     * @return buyerId  - the buyer id\n     */\n    function getBuyerIdByWallet(address _wallet) internal view returns (bool exists, uint256 buyerId) {\n        // Get the buyer id\n        buyerId = protocolLookups().buyerIdByWallet[_wallet];\n\n        // Determine existence\n        exists = (buyerId > 0);\n    }\n\n    /**\n     * @notice Gets a agent id from storage by wallet address\n     *\n     * @param _wallet - the wallet address of the buyer\n     * @return exists - whether the buyer id exists\n     * @return agentId  - the buyer id\n     */\n    function getAgentIdByWallet(address _wallet) internal view returns (bool exists, uint256 agentId) {\n        // Get the buyer id\n        agentId = protocolLookups().agentIdByWallet[_wallet];\n\n        // Determine existence\n        exists = (agentId > 0);\n    }\n\n    /**\n     * @notice Gets a dispute resolver id from storage by assistant address\n     *\n     * @param _assistant - the assistant address of the dispute resolver\n     * @return exists - whether the dispute resolver id exists\n     * @return disputeResolverId  - the dispute resolver  id\n     */\n    function getDisputeResolverIdByAssistant(\n        address _assistant\n    ) internal view returns (bool exists, uint256 disputeResolverId) {\n        // Get the dispute resolver id\n        disputeResolverId = protocolLookups().disputeResolverIdByAssistant[_assistant];\n\n        // Determine existence\n        exists = (disputeResolverId > 0);\n    }\n\n    /**\n     * @notice Gets a dispute resolver id from storage by admin address\n     *\n     * @param _admin - the admin address of the dispute resolver\n     * @return exists - whether the dispute resolver id exists\n     * @return disputeResolverId  - the dispute resolver id\n     */\n    function getDisputeResolverIdByAdmin(\n        address _admin\n    ) internal view returns (bool exists, uint256 disputeResolverId) {\n        // Get the dispute resolver id\n        disputeResolverId = protocolLookups().disputeResolverIdByAdmin[_admin];\n\n        // Determine existence\n        exists = (disputeResolverId > 0);\n    }\n\n    /**\n     * @notice Gets a group id from storage by offer id\n     *\n     * @param _offerId - the offer id\n     * @return exists - whether the group id exists\n     * @return groupId  - the group id.\n     */\n    function getGroupIdByOffer(uint256 _offerId) internal view returns (bool exists, uint256 groupId) {\n        // Get the group id\n        groupId = protocolLookups().groupIdByOffer[_offerId];\n\n        // Determine existence\n        exists = (groupId > 0);\n    }\n\n    /**\n     * @notice Fetches a given seller from storage by id\n     *\n     * @param _sellerId - the id of the seller\n     * @return exists - whether the seller exists\n     * @return seller - the seller details. See {BosonTypes.Seller}\n     * @return authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the user can use to do admin functions\n     */\n    function fetchSeller(\n        uint256 _sellerId\n    ) internal view returns (bool exists, Seller storage seller, AuthToken storage authToken) {\n        // Cache protocol entities for reference\n        ProtocolLib.ProtocolEntities storage entities = protocolEntities();\n\n        // Get the seller's slot\n        seller = entities.sellers[_sellerId];\n\n        //Get the seller's auth token's slot\n        authToken = entities.authTokens[_sellerId];\n\n        // Determine existence\n        exists = (_sellerId > 0 && seller.id == _sellerId);\n    }\n\n    /**\n     * @notice Fetches a given buyer from storage by id\n     *\n     * @param _buyerId - the id of the buyer\n     * @return exists - whether the buyer exists\n     * @return buyer - the buyer details. See {BosonTypes.Buyer}\n     */\n    function fetchBuyer(uint256 _buyerId) internal view returns (bool exists, BosonTypes.Buyer storage buyer) {\n        // Get the buyer's slot\n        buyer = protocolEntities().buyers[_buyerId];\n\n        // Determine existence\n        exists = (_buyerId > 0 && buyer.id == _buyerId);\n    }\n\n    /**\n     * @notice Fetches a given dispute resolver from storage by id\n     *\n     * @param _disputeResolverId - the id of the dispute resolver\n     * @return exists - whether the dispute resolver exists\n     * @return disputeResolver - the dispute resolver details. See {BosonTypes.DisputeResolver}\n     * @return disputeResolverFees - list of fees dispute resolver charges per token type. Zero address is native currency. See {BosonTypes.DisputeResolverFee}\n     */\n    function fetchDisputeResolver(\n        uint256 _disputeResolverId\n    )\n        internal\n        view\n        returns (\n            bool exists,\n            BosonTypes.DisputeResolver storage disputeResolver,\n            BosonTypes.DisputeResolverFee[] storage disputeResolverFees\n        )\n    {\n        // Cache protocol entities for reference\n        ProtocolLib.ProtocolEntities storage entities = protocolEntities();\n\n        // Get the dispute resolver's slot\n        disputeResolver = entities.disputeResolvers[_disputeResolverId];\n\n        //Get dispute resolver's fee list slot\n        disputeResolverFees = entities.disputeResolverFees[_disputeResolverId];\n\n        // Determine existence\n        exists = (_disputeResolverId > 0 && disputeResolver.id == _disputeResolverId);\n    }\n\n    /**\n     * @notice Fetches a given agent from storage by id\n     *\n     * @param _agentId - the id of the agent\n     * @return exists - whether the agent exists\n     * @return agent - the agent details. See {BosonTypes.Agent}\n     */\n    function fetchAgent(uint256 _agentId) internal view returns (bool exists, BosonTypes.Agent storage agent) {\n        // Get the agent's slot\n        agent = protocolEntities().agents[_agentId];\n\n        // Determine existence\n        exists = (_agentId > 0 && agent.id == _agentId);\n    }\n\n    /**\n     * @notice Fetches a given offer from storage by id\n     *\n     * @param _offerId - the id of the offer\n     * @return exists - whether the offer exists\n     * @return offer - the offer details. See {BosonTypes.Offer}\n     */\n    function fetchOffer(uint256 _offerId) internal view returns (bool exists, Offer storage offer) {\n        // Get the offer's slot\n        offer = protocolEntities().offers[_offerId];\n\n        // Determine existence\n        exists = (_offerId > 0 && offer.id == _offerId);\n    }\n\n    /**\n     * @notice Fetches the offer dates from storage by offer id\n     *\n     * @param _offerId - the id of the offer\n     * @return offerDates - the offer dates details. See {BosonTypes.OfferDates}\n     */\n    function fetchOfferDates(uint256 _offerId) internal view returns (BosonTypes.OfferDates storage offerDates) {\n        // Get the offerDates slot\n        offerDates = protocolEntities().offerDates[_offerId];\n    }\n\n    /**\n     * @notice Fetches the offer durations from storage by offer id\n     *\n     * @param _offerId - the id of the offer\n     * @return offerDurations - the offer durations details. See {BosonTypes.OfferDurations}\n     */\n    function fetchOfferDurations(\n        uint256 _offerId\n    ) internal view returns (BosonTypes.OfferDurations storage offerDurations) {\n        // Get the offer's slot\n        offerDurations = protocolEntities().offerDurations[_offerId];\n    }\n\n    /**\n     * @notice Fetches the dispute resolution terms from storage by offer id\n     *\n     * @param _offerId - the id of the offer\n     * @return disputeResolutionTerms - the details about the dispute resolution terms. See {BosonTypes.DisputeResolutionTerms}\n     */\n    function fetchDisputeResolutionTerms(\n        uint256 _offerId\n    ) internal view returns (BosonTypes.DisputeResolutionTerms storage disputeResolutionTerms) {\n        // Get the disputeResolutionTerms slot\n        disputeResolutionTerms = protocolEntities().disputeResolutionTerms[_offerId];\n    }\n\n    /**\n     * @notice Fetches a given group from storage by id\n     *\n     * @param _groupId - the id of the group\n     * @return exists - whether the group exists\n     * @return group - the group details. See {BosonTypes.Group}\n     */\n    function fetchGroup(uint256 _groupId) internal view returns (bool exists, Group storage group) {\n        // Get the group's slot\n        group = protocolEntities().groups[_groupId];\n\n        // Determine existence\n        exists = (_groupId > 0 && group.id == _groupId);\n    }\n\n    /**\n     * @notice Fetches the Condition from storage by group id\n     *\n     * @param _groupId - the id of the group\n     * @return condition - the condition details. See {BosonTypes.Condition}\n     */\n    function fetchCondition(uint256 _groupId) internal view returns (BosonTypes.Condition storage condition) {\n        // Get the offerDates slot\n        condition = protocolEntities().conditions[_groupId];\n    }\n\n    /**\n     * @notice Fetches a given exchange from storage by id\n     *\n     * @param _exchangeId - the id of the exchange\n     * @return exists - whether the exchange exists\n     * @return exchange - the exchange details. See {BosonTypes.Exchange}\n     */\n    function fetchExchange(uint256 _exchangeId) internal view returns (bool exists, Exchange storage exchange) {\n        // Get the exchange's slot\n        exchange = protocolEntities().exchanges[_exchangeId];\n\n        // Determine existence\n        exists = (_exchangeId > 0 && exchange.id == _exchangeId);\n    }\n\n    /**\n     * @notice Fetches a given voucher from storage by exchange id\n     *\n     * @param _exchangeId - the id of the exchange associated with the voucher\n     * @return voucher - the voucher details. See {BosonTypes.Voucher}\n     */\n    function fetchVoucher(uint256 _exchangeId) internal view returns (Voucher storage voucher) {\n        // Get the voucher\n        voucher = protocolEntities().vouchers[_exchangeId];\n    }\n\n    /**\n     * @notice Fetches a given dispute from storage by exchange id\n     *\n     * @param _exchangeId - the id of the exchange associated with the dispute\n     * @return exists - whether the dispute exists\n     * @return dispute - the dispute details. See {BosonTypes.Dispute}\n     */\n    function fetchDispute(\n        uint256 _exchangeId\n    ) internal view returns (bool exists, Dispute storage dispute, DisputeDates storage disputeDates) {\n        // Cache protocol entities for reference\n        ProtocolLib.ProtocolEntities storage entities = protocolEntities();\n\n        // Get the dispute's slot\n        dispute = entities.disputes[_exchangeId];\n\n        // Get the disputeDates slot\n        disputeDates = entities.disputeDates[_exchangeId];\n\n        // Determine existence\n        exists = (_exchangeId > 0 && dispute.exchangeId == _exchangeId);\n    }\n\n    /**\n     * @notice Fetches a given twin from storage by id\n     *\n     * @param _twinId - the id of the twin\n     * @return exists - whether the twin exists\n     * @return twin - the twin details. See {BosonTypes.Twin}\n     */\n    function fetchTwin(uint256 _twinId) internal view returns (bool exists, Twin storage twin) {\n        // Get the twin's slot\n        twin = protocolEntities().twins[_twinId];\n\n        // Determine existence\n        exists = (_twinId > 0 && twin.id == _twinId);\n    }\n\n    /**\n     * @notice Fetches a given bundle from storage by id\n     *\n     * @param _bundleId - the id of the bundle\n     * @return exists - whether the bundle exists\n     * @return bundle - the bundle details. See {BosonTypes.Bundle}\n     */\n    function fetchBundle(uint256 _bundleId) internal view returns (bool exists, Bundle storage bundle) {\n        // Get the bundle's slot\n        bundle = protocolEntities().bundles[_bundleId];\n\n        // Determine existence\n        exists = (_bundleId > 0 && bundle.id == _bundleId);\n    }\n\n    /**\n     * @notice Gets offer from protocol storage, makes sure it exist and not voided\n     *\n     * Reverts if:\n     * - Offer does not exist\n     * - Offer already voided\n     *\n     *  @param _offerId - the id of the offer to check\n     */\n    function getValidOffer(uint256 _offerId) internal view returns (Offer storage offer) {\n        bool exists;\n\n        // Get offer\n        (exists, offer) = fetchOffer(_offerId);\n\n        // Offer must already exist\n        if (!exists) revert NoSuchOffer();\n\n        // Offer must not already be voided\n        if (offer.voided) revert OfferHasBeenVoided();\n    }\n\n    /**\n     * @notice Gets offer and seller from protocol storage\n     *\n     * Reverts if:\n     * - Offer does not exist\n     * - Offer already voided\n     * - Seller assistant is not the caller\n     *\n     *  @param _offerId - the id of the offer to check\n     *  @return offer - the offer details. See {BosonTypes.Offer}\n     */\n    function getValidOfferWithSellerCheck(uint256 _offerId) internal view returns (Offer storage offer) {\n        // Get offer\n        offer = getValidOffer(_offerId);\n\n        // Get seller, we assume seller exists if offer exists\n        (, Seller storage seller, ) = fetchSeller(offer.sellerId);\n\n        // Caller must be seller's assistant address\n        if (seller.assistant != msgSender()) revert NotAssistant();\n    }\n\n    /**\n     * @notice Gets the bundle id for a given offer id.\n     *\n     * @param _offerId - the offer id.\n     * @return exists - whether the bundle id exists\n     * @return bundleId  - the bundle id.\n     */\n    function fetchBundleIdByOffer(uint256 _offerId) internal view returns (bool exists, uint256 bundleId) {\n        // Get the bundle id\n        bundleId = protocolLookups().bundleIdByOffer[_offerId];\n\n        // Determine existence\n        exists = (bundleId > 0);\n    }\n\n    /**\n     * @notice Gets the bundle id for a given twin id.\n     *\n     * @param _twinId - the twin id.\n     * @return exists - whether the bundle id exist\n     * @return bundleId  - the bundle id.\n     */\n    function fetchBundleIdByTwin(uint256 _twinId) internal view returns (bool exists, uint256 bundleId) {\n        // Get the bundle id\n        bundleId = protocolLookups().bundleIdByTwin[_twinId];\n\n        // Determine existence\n        exists = (bundleId > 0);\n    }\n\n    /**\n     * @notice Gets the exchange ids for a given offer id.\n     *\n     * @param _offerId - the offer id.\n     * @return exists - whether the exchange Ids exist\n     * @return exchangeIds  - the exchange Ids.\n     */\n    function getExchangeIdsByOffer(\n        uint256 _offerId\n    ) internal view returns (bool exists, uint256[] storage exchangeIds) {\n        // Get the exchange Ids\n        exchangeIds = protocolLookups().exchangeIdsByOffer[_offerId];\n\n        // Determine existence\n        exists = (exchangeIds.length > 0);\n    }\n\n    /**\n     * @notice Make sure the caller is buyer associated with the exchange\n     *\n     * Reverts if\n     * - caller is not the buyer associated with exchange\n     *\n     * @param _currentBuyer - id of current buyer associated with the exchange\n     */\n    function checkBuyer(uint256 _currentBuyer) internal view {\n        // Get the caller's buyer account id\n        (, uint256 buyerId) = getBuyerIdByWallet(msgSender());\n\n        // Must be the buyer associated with the exchange (which is always voucher holder)\n        if (buyerId != _currentBuyer) revert NotVoucherHolder();\n    }\n\n    /**\n     * @notice Get a valid exchange and its associated voucher\n     *\n     * Reverts if\n     * - Exchange does not exist\n     * - Exchange is not in the expected state\n     *\n     * @param _exchangeId - the id of the exchange to complete\n     * @param _expectedState - the state the exchange should be in\n     * @return exchange - the exchange\n     * @return voucher - the voucher\n     */\n    function getValidExchange(\n        uint256 _exchangeId,\n        ExchangeState _expectedState\n    ) internal view returns (Exchange storage exchange, Voucher storage voucher) {\n        // Get the exchange\n        bool exchangeExists;\n        (exchangeExists, exchange) = fetchExchange(_exchangeId);\n\n        // Make sure the exchange exists\n        if (!exchangeExists) revert NoSuchExchange();\n\n        // Make sure the exchange is in expected state\n        if (exchange.state != _expectedState) revert InvalidState();\n\n        // Get the voucher\n        voucher = fetchVoucher(_exchangeId);\n    }\n\n    /**\n     * @notice Returns the current sender address.\n     */\n    function msgSender() internal view returns (address) {\n        return EIP712Lib.msgSender();\n    }\n\n    /**\n     * @notice Gets the agent id for a given offer id.\n     *\n     * @param _offerId - the offer id.\n     * @return exists - whether the exchange id exist\n     * @return agentId - the agent id.\n     */\n    function fetchAgentIdByOffer(uint256 _offerId) internal view returns (bool exists, uint256 agentId) {\n        // Get the agent id\n        agentId = protocolLookups().agentIdByOffer[_offerId];\n\n        // Determine existence\n        exists = (agentId > 0);\n    }\n\n    /**\n     * @notice Fetches the offer fees from storage by offer id\n     *\n     * @param _offerId - the id of the offer\n     * @return offerFees - the offer fees details. See {BosonTypes.OfferFees}\n     */\n    function fetchOfferFees(uint256 _offerId) internal view returns (BosonTypes.OfferFees storage offerFees) {\n        // Get the offerFees slot\n        offerFees = protocolEntities().offerFees[_offerId];\n    }\n\n    /**\n     * @notice Fetches a list of twin receipts from storage by exchange id\n     *\n     * @param _exchangeId - the id of the exchange\n     * @return exists - whether one or more twin receipt exists\n     * @return twinReceipts - the list of twin receipts. See {BosonTypes.TwinReceipt}\n     */\n    function fetchTwinReceipts(\n        uint256 _exchangeId\n    ) internal view returns (bool exists, TwinReceipt[] storage twinReceipts) {\n        // Get the twin receipts slot\n        twinReceipts = protocolLookups().twinReceiptsByExchange[_exchangeId];\n\n        // Determine existence\n        exists = (_exchangeId > 0 && twinReceipts.length > 0);\n    }\n\n    /**\n     * @notice Fetches a condition from storage by exchange id\n     *\n     * @param _exchangeId - the id of the exchange\n     * @return exists - whether one condition exists for the exchange\n     * @return condition - the condition. See {BosonTypes.Condition}\n     */\n    function fetchConditionByExchange(\n        uint256 _exchangeId\n    ) internal view returns (bool exists, Condition storage condition) {\n        // Get the condition slot\n        condition = protocolLookups().exchangeCondition[_exchangeId];\n\n        // Determine existence\n        exists = (_exchangeId > 0 && condition.method != EvaluationMethod.None);\n    }\n\n    /**\n     * @notice calculate the protocol fee for a given exchange\n     *\n     * @param _exchangeToken - the token used for the exchange\n     * @param _price - the price of the exchange\n     * @return protocolFee - the protocol fee\n     */\n    function getProtocolFee(address _exchangeToken, uint256 _price) internal view returns (uint256 protocolFee) {\n        // Calculate and set the protocol fee\n        return\n            _exchangeToken == protocolAddresses().token\n                ? protocolFees().flatBoson\n                : (protocolFees().percentage * _price) / HUNDRED_PERCENT;\n    }\n\n    /**\n     * @notice Fetches a clone address from storage by seller id and collection index\n     * If the collection index is 0, the clone address is the seller's main collection,\n     * otherwise it is the clone address of the additional collection at the given index.\n     *\n     * @param _lookups - storage slot for protocol lookups\n     * @param _sellerId - the id of the seller\n     * @param _collectionIndex - the index of the collection\n     * @return cloneAddress - the clone address\n     */\n    function getCloneAddress(\n        ProtocolLib.ProtocolLookups storage _lookups,\n        uint256 _sellerId,\n        uint256 _collectionIndex\n    ) internal view returns (address cloneAddress) {\n        return\n            _collectionIndex == 0\n                ? _lookups.cloneAddress[_sellerId]\n                : _lookups.additionalCollections[_sellerId][_collectionIndex - 1].collectionAddress;\n    }\n\n    /**\n     * @notice Internal helper to get royalty information and seller for a chosen exchange.\n     *\n     * Reverts if exchange does not exist.\n     *\n     * @param _queryId - offer id or exchange id\n     * @param _isExchangeId - indicates if the query represents the exchange id\n     * @return royaltyInfo - list of royalty recipients and corresponding bps\n     * @return royaltyInfoIndex - index of the royalty info\n     * @return treasury - the seller's treasury address\n     */\n    function fetchRoyalties(\n        uint256 _queryId,\n        bool _isExchangeId\n    ) internal view returns (RoyaltyInfo storage royaltyInfo, uint256 royaltyInfoIndex, address treasury) {\n        RoyaltyInfo[] storage royaltyInfoAll;\n        if (_isExchangeId) {\n            (bool exists, Exchange storage exchange) = fetchExchange(_queryId);\n            if (!exists) revert NoSuchExchange();\n            _queryId = exchange.offerId;\n        }\n\n        // not using fetchOffer to reduce gas costs (limitation of royalty registry)\n        ProtocolLib.ProtocolEntities storage pe = protocolEntities();\n        Offer storage offer = pe.offers[_queryId];\n        treasury = pe.sellers[offer.sellerId].treasury;\n        royaltyInfoAll = pe.offers[_queryId].royaltyInfo;\n\n        uint256 royaltyInfoLength = royaltyInfoAll.length;\n        if (royaltyInfoLength == 0) revert NoSuchOffer();\n        royaltyInfoIndex = royaltyInfoLength - 1;\n        // get the last royalty info\n        return (royaltyInfoAll[royaltyInfoIndex], royaltyInfoIndex, treasury);\n    }\n\n    /**\n     * @notice Helper function that calculates the total royalty percentage for a given exchange\n     *\n     * @param _bps - storage slot for array of royalty percentages\n     * @return totalBps - the total royalty percentage\n     */\n    function getTotalRoyaltyPercentage(uint256[] storage _bps) internal view returns (uint256 totalBps) {\n        uint256 bpsLength = _bps.length;\n        for (uint256 i = 0; i < bpsLength; ) {\n            totalBps += _bps[i];\n\n            unchecked {\n                i++;\n            }\n        }\n    }\n}\n"
      },
      "contracts/protocol/bases/ReentrancyGuardBase.sol": {
        "content": "// SPDX-License-Identifier: MIT\nimport \"../../domain/BosonConstants.sol\";\nimport { BosonErrors } from \"../../domain/BosonErrors.sol\";\nimport { ProtocolLib } from \"../libs/ProtocolLib.sol\";\n\npragma solidity 0.8.22;\n\n/**\n * @notice Contract module that helps prevent reentrant calls to a function.\n *\n * The majority of code, comments and general idea is taken from OpenZeppelin implementation.\n * Code was adjusted to work with the storage layout used in the protocol.\n * Reference implementation: OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * @dev Because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardBase {\n    /**\n     * @notice Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        ProtocolLib.ProtocolStatus storage ps = ProtocolLib.protocolStatus();\n        // On the first call to nonReentrant, ps.reentrancyStatus will be NOT_ENTERED\n        if (ps.reentrancyStatus == ENTERED) revert BosonErrors.ReentrancyGuard();\n\n        // Any calls to nonReentrant after this point will fail\n        ps.reentrancyStatus = ENTERED;\n\n        _;\n\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        ps.reentrancyStatus = NOT_ENTERED;\n    }\n}\n"
      },
      "contracts/protocol/bases/SellerBase.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport \"./../../domain/BosonConstants.sol\";\nimport { IBosonAccountEvents } from \"../../interfaces/events/IBosonAccountEvents.sol\";\nimport { ProtocolBase } from \"./ProtocolBase.sol\";\nimport { ProtocolLib } from \"./../libs/ProtocolLib.sol\";\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\nimport { IInitializableVoucherClone } from \"../../interfaces/IInitializableVoucherClone.sol\";\nimport { IERC721 } from \"../../interfaces/IERC721.sol\";\n\n/**\n * @title SellerBase\n *\n * @dev Provides methods for seller creation that can be shared across facets\n */\ncontract SellerBase is ProtocolBase, IBosonAccountEvents {\n    /**\n     * @notice Creates a seller.\n     *\n     * Emits a SellerCreated event if successful.\n     *\n     * Reverts if:\n     * - Caller is not the supplied assistant\n     * - Supplied clerk is not a zero address\n     * - The sellers region of protocol is paused\n     * - Address values are zero address\n     * - Addresses are not unique to this seller\n     * - Caller is not the admin address of the stored seller\n     * - Caller is not the address of the owner of the stored AuthToken\n     * - Seller is not active (if active == false)\n     * - Admin address is zero address and AuthTokenType == None\n     * - AuthTokenType is not unique to this seller\n     * - AuthTokenType is Custom\n     * - Seller salt is not unique\n     * - Clone creation fails\n     *\n     * @param _seller - the fully populated struct with seller id set to 0x0\n     * @param _authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     * @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct\n     */\n    function createSellerInternal(\n        Seller memory _seller,\n        AuthToken calldata _authToken,\n        VoucherInitValues calldata _voucherInitValues\n    ) internal {\n        // Check active is not set to false\n        if (!_seller.active) revert MustBeActive();\n\n        // Check for zero address\n        if (_seller.assistant == address(0) || _seller.treasury == address(0)) revert InvalidAddress();\n\n        // Admin address or AuthToken data must be present. A seller can have one or the other\n        if (\n            (_seller.admin == address(0) && _authToken.tokenType == AuthTokenType.None) ||\n            (_seller.admin != address(0) && _authToken.tokenType != AuthTokenType.None)\n        ) revert AdminOrAuthToken();\n\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Get message sender\n        address sender = msgSender();\n\n        // Check that caller is the supplied assistant\n        if (_seller.assistant != sender) revert NotAssistant();\n        if (_seller.clerk != address(0)) revert ClerkDeprecated();\n\n        // Do caller and uniqueness checks based on auth type\n        if (_authToken.tokenType != AuthTokenType.None) {\n            if (_authToken.tokenType == AuthTokenType.Custom) revert InvalidAuthTokenType();\n\n            // Check that caller owns the auth token\n            address authTokenContract = lookups.authTokenContracts[_authToken.tokenType];\n            address tokenIdOwner = IERC721(authTokenContract).ownerOf(_authToken.tokenId);\n            if (tokenIdOwner != sender) revert NotAdmin();\n\n            // Check that auth token is unique to this seller\n            if (lookups.sellerIdByAuthToken[_authToken.tokenType][_authToken.tokenId] != 0)\n                revert AuthTokenMustBeUnique();\n        } else {\n            // Check that caller is supplied admin\n            if (_seller.admin != sender) revert NotAdmin();\n        }\n\n        // Check that the sender address is unique to one seller id, across all roles\n        if (lookups.sellerIdByAdmin[sender] != 0 || lookups.sellerIdByAssistant[sender] != 0)\n            revert SellerAddressMustBeUnique();\n\n        // Get the next account id and increment the counter\n        uint256 sellerId = protocolCounters().nextAccountId++;\n        _seller.id = sellerId;\n        storeSeller(_seller, _authToken, lookups);\n\n        // Set treasury as the default royalty recipient\n        if (_voucherInitValues.royaltyPercentage > protocolLimits().maxRoyaltyPercentage)\n            revert InvalidRoyaltyPercentage();\n        RoyaltyRecipientInfo[] storage royaltyRecipients = lookups.royaltyRecipientsBySeller[sellerId];\n        RoyaltyRecipientInfo storage defaultRoyaltyRecipient = royaltyRecipients.push();\n        // We don't store the defaultRoyaltyRecipient.wallet, since it's always the trasury\n        defaultRoyaltyRecipient.minRoyaltyPercentage = _voucherInitValues.royaltyPercentage;\n\n        // Calculate seller salt and check that it is unique\n        bytes32 sellerSalt = keccak256(abi.encodePacked(sender, _voucherInitValues.collectionSalt));\n        if (lookups.isUsedSellerSalt[sellerSalt]) revert SellerSaltNotUnique();\n        lookups.sellerSalt[sellerId] = sellerSalt;\n        lookups.isUsedSellerSalt[sellerSalt] = true;\n\n        // Create clone and store its address cloneAddress\n        address voucherCloneAddress = cloneBosonVoucher(sellerId, 0, sellerSalt, _seller.assistant, _voucherInitValues);\n        lookups.cloneAddress[sellerId] = voucherCloneAddress;\n\n        // Notify watchers of state change\n        emit SellerCreated(sellerId, _seller, voucherCloneAddress, _authToken, sender);\n        emit RoyaltyRecipientsChanged(sellerId, fetchRoyaltyRecipients(sellerId), sender);\n    }\n\n    /**\n     * @notice Validates seller struct and stores it to storage, along with auth token if present.\n     *\n     * @param _seller - the fully populated struct with seller id set\n     * @param _authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     * @param _lookups - ProtocolLib.ProtocolLookups struct\n     */\n    function storeSeller(\n        Seller memory _seller,\n        AuthToken calldata _authToken,\n        ProtocolLib.ProtocolLookups storage _lookups\n    ) internal {\n        // Get storage location for seller\n        (, Seller storage seller, AuthToken storage authToken) = fetchSeller(_seller.id);\n\n        // Set seller props individually since memory structs can't be copied to storage\n        seller.id = _seller.id;\n        seller.assistant = _seller.assistant;\n        seller.admin = _seller.admin;\n        seller.treasury = _seller.treasury;\n        seller.active = _seller.active;\n        seller.metadataUri = _seller.metadataUri;\n\n        // Auth token passed in\n        if (_authToken.tokenType != AuthTokenType.None) {\n            // Store auth token\n            authToken.tokenId = _authToken.tokenId;\n            authToken.tokenType = _authToken.tokenType;\n\n            // Store seller by auth token reference\n            _lookups.sellerIdByAuthToken[_authToken.tokenType][_authToken.tokenId] = _seller.id;\n        } else {\n            // Empty auth token passed in\n            // Store admin address reference\n            _lookups.sellerIdByAdmin[_seller.admin] = _seller.id;\n        }\n\n        // Map the seller's other addresses to the seller id. It's not necessary to map the treasury address, as it only receives funds\n        _lookups.sellerIdByAssistant[_seller.assistant] = _seller.id;\n    }\n\n    /**\n     * @notice Creates a minimal clone of the Boson Voucher Contract.\n     *\n     * Reverts if clone creation fails.\n     *\n     * @param _sellerId - id of the seller\n     * @param _collectionIndex - index of the collection.\n     * @param _sellerSalt - seller dependent salt, used to create the clone address\n     * @param _assistant - address of the assistant\n     * @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct\n     * @return cloneAddress - the address of newly created clone\n     */\n    function cloneBosonVoucher(\n        uint256 _sellerId,\n        uint256 _collectionIndex,\n        bytes32 _sellerSalt,\n        address _assistant,\n        VoucherInitValues calldata _voucherInitValues\n    ) internal returns (address cloneAddress) {\n        // Pointer to stored addresses\n        ProtocolLib.ProtocolAddresses storage pa = protocolAddresses();\n\n        // Load beacon proxy contract address\n        bytes20 targetBytes = bytes20(pa.beaconProxy);\n        bytes32 collectionSalt = keccak256(abi.encodePacked(_sellerSalt, _voucherInitValues.collectionSalt));\n\n        // create a minimal clone\n        assembly {\n            let clone := mload(0x40)\n            mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)\n            mstore(add(clone, 0x14), targetBytes)\n            mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)\n            cloneAddress := create2(0, clone, 0x37, collectionSalt)\n        }\n\n        if (cloneAddress == address(0)) revert CloneCreationFailed();\n\n        // Initialize the clone\n        IInitializableVoucherClone(cloneAddress).initialize(pa.voucherBeacon);\n        IInitializableVoucherClone(cloneAddress).initializeVoucher(\n            _sellerId,\n            _collectionIndex,\n            _assistant,\n            _voucherInitValues\n        );\n    }\n\n    /**\n     * @notice Fetches a given seller pending update from storage by id\n     *\n     * @param _sellerId - the id of the seller\n     * @return exists - whether the seller or auth token pending update exists\n     * @return sellerPendingUpdate - the seller pending update details. See {BosonTypes.Seller}\n     * @return authTokenPendingUpdate - auth token pending update details\n     */\n    function fetchSellerPendingUpdate(\n        uint256 _sellerId\n    )\n        internal\n        view\n        returns (bool exists, Seller storage sellerPendingUpdate, AuthToken storage authTokenPendingUpdate)\n    {\n        // Cache protocol entities for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Get the seller pending update slot\n        sellerPendingUpdate = lookups.pendingAddressUpdatesBySeller[_sellerId];\n\n        //Get the seller auth token pending update slot\n        authTokenPendingUpdate = lookups.pendingAuthTokenUpdatesBySeller[_sellerId];\n\n        // Determine existence\n        exists =\n            sellerPendingUpdate.admin != address(0) ||\n            sellerPendingUpdate.assistant != address(0) ||\n            authTokenPendingUpdate.tokenType != AuthTokenType.None;\n    }\n\n    /**\n     * @notice Gets seller's royalty recipients.\n     *\n     * @param _sellerId - seller id\n     * @return royaltyRecipients - list of royalty recipients\n     */\n    function fetchRoyaltyRecipients(\n        uint256 _sellerId\n    ) internal view returns (RoyaltyRecipientInfo[] memory royaltyRecipients) {\n        return protocolLookups().royaltyRecipientsBySeller[_sellerId];\n    }\n}\n"
      },
      "contracts/protocol/facets/SellerHandlerFacet.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\nimport \"../../domain/BosonConstants.sol\";\nimport { IBosonVoucher } from \"../../interfaces/clients/IBosonVoucher.sol\";\nimport { IBosonSellerHandler } from \"../../interfaces/handlers/IBosonSellerHandler.sol\";\nimport { SellerBase } from \"../bases/SellerBase.sol\";\nimport { ProtocolLib } from \"../libs/ProtocolLib.sol\";\nimport { IERC721 } from \"../../interfaces/IERC721.sol\";\nimport { Create2 } from \"@openzeppelin/contracts/utils/Create2.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title SellerHandlerFacet\n *\n * @notice Handles Seller account management requests and queries.\n */\ncontract SellerHandlerFacet is IBosonSellerHandler, SellerBase {\n    /**\n     * @notice Initializes facet.\n     */\n    function initialize() public {\n        // No-op initializer.\n        // - needed by the deployment script, which expects a no-args initializer on facets other than the config handler\n        // - exception here because IBosonAccountHandler is contributed to by multiple facets which do not have their own individual interfaces\n    }\n\n    /**\n     * @notice Creates a seller.\n     *\n     * Emits a SellerCreated event if successful.\n     *\n     * Reverts if:\n     * - Caller is not the supplied admin or does not own supplied auth token\n     * - Caller is not the supplied assistant\n     * - Supplied clerk is not a zero address\n     * - The sellers region of protocol is paused\n     * - Address values are zero address\n     * - Addresses are not unique to this seller\n     * - Seller is not active (if active == false)\n     * - Admin address is zero address and AuthTokenType == None\n     * - AuthTokenType is not unique to this seller\n     * - AuthTokenType is Custom\n     * - Seller salt is not unique\n     * - Clone creation fails\n     *\n     * @param _seller - the fully populated struct with seller id set to 0x0\n     * @param _authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     * @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct\n     */\n    function createSeller(\n        Seller memory _seller,\n        AuthToken calldata _authToken,\n        VoucherInitValues calldata _voucherInitValues\n    ) external sellersNotPaused nonReentrant {\n        // create seller and update structs values to represent true state\n        createSellerInternal(_seller, _authToken, _voucherInitValues);\n    }\n\n    /**\n     * @notice Updates treasury address, if changed. Puts admin, assistant and AuthToken in pending queue, if changed.\n     *         Pending updates can be completed by calling the optInToSellerUpdate function.\n     * @dev    Active flag passed in by caller will be ignored. The value from storage will be used.\n     *\n     * Emits a SellerUpdateApplied event if the seller has changed the treasury.\n     * Emits a SellerUpdatePending event if the seller has requested an update for admin, assistant, or auth token.\n     * Holder of new auth token and/or owner(s) of new addresses for admin, assistant must opt-in to the update.\n     *\n     * Reverts if:\n     * - The sellers region of protocol is paused\n     * - Address values are zero address\n     * - Addresses are not unique to this seller\n     * - Supplied clerk is not a zero address\n     * - Caller address is not the admin address of the stored seller with no AuthToken\n     * - Caller is not the owner of the seller's stored AuthToken\n     * - Seller does not exist\n     * - Admin address is zero address and AuthTokenType == None\n     * - AuthTokenType is not unique to this seller\n     * - AuthTokenType is Custom\n     * - No field has been updated or requested to be updated\n     *\n     * @param _seller - the fully populated seller struct\n     * @param _authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     */\n    function updateSeller(Seller memory _seller, AuthToken calldata _authToken) external sellersNotPaused nonReentrant {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Admin address or AuthToken data must be present. A seller can have one or the other\n        if (\n            (_seller.admin == address(0) && _authToken.tokenType == AuthTokenType.None) ||\n            (_seller.admin != address(0) && _authToken.tokenType != AuthTokenType.None)\n        ) {\n            revert AdminOrAuthToken();\n        }\n        if (_seller.clerk != address(0)) revert ClerkDeprecated();\n\n        if (_authToken.tokenType == AuthTokenType.Custom) revert InvalidAuthTokenType();\n\n        // Check Seller exists in sellers mapping\n        Seller storage seller;\n        AuthToken storage authToken;\n        {\n            bool exists;\n            (exists, seller, authToken) = fetchSeller(_seller.id);\n\n            // Seller must already exist\n            if (!exists) revert NoSuchSeller();\n        }\n        // Get message sender\n        // address sender = msgSender(); // temporary disabled due to stack too deep error. Revisit when compiler version is upgraded\n\n        // Check that caller is authorized to call this function\n        authorizeAdmin(lookups, authToken, seller.admin, msgSender());\n\n        // Clean old seller pending update data if exists\n        delete lookups.pendingAddressUpdatesBySeller[_seller.id];\n        delete lookups.pendingAuthTokenUpdatesBySeller[_seller.id];\n\n        (, Seller storage sellerPendingUpdate, AuthToken storage authTokenPendingUpdate) = fetchSellerPendingUpdate(\n            _seller.id\n        );\n\n        {\n            bool needsApproval;\n            // Admin address or AuthToken data must be present in parameters. A seller can have one or the other. Check passed in parameters\n            if (_authToken.tokenType != AuthTokenType.None) {\n                // If AuthToken data is different from the one in storage, then set it as pending update\n                if (authToken.tokenType != _authToken.tokenType || authToken.tokenId != _authToken.tokenId) {\n                    // Check that auth token is unique to this seller\n                    if (lookups.sellerIdByAuthToken[_authToken.tokenType][_authToken.tokenId] != 0)\n                        revert AuthTokenMustBeUnique();\n\n                    // Auth token owner must approve the update to prevent front-running\n                    authTokenPendingUpdate.tokenType = _authToken.tokenType;\n                    authTokenPendingUpdate.tokenId = _authToken.tokenId;\n                    needsApproval = true;\n                }\n            } else if (_seller.admin != seller.admin) {\n                preUpdateSellerCheck(_seller.id, _seller.admin, lookups);\n                // If admin address exists, admin address owner must approve the update to prevent front-running\n                sellerPendingUpdate.admin = _seller.admin;\n                needsApproval = true;\n            }\n\n            if (_seller.assistant != seller.assistant) {\n                preUpdateSellerCheck(_seller.id, _seller.assistant, lookups);\n                if (_seller.assistant == address(0)) revert InvalidAddress();\n                // Assistant address owner must approve the update to prevent front-running\n                sellerPendingUpdate.assistant = _seller.assistant;\n                needsApproval = true;\n            }\n\n            bool updateApplied;\n\n            if (_seller.treasury != seller.treasury) {\n                if (_seller.treasury == address(0)) revert InvalidAddress();\n\n                // Check if new treasury is already a royalty recipient\n                mapping(address => uint256) storage royaltyRecipientIndexBySellerAndRecipient = lookups\n                    .royaltyRecipientIndexBySellerAndRecipient[_seller.id];\n                uint256 royaltyRecipientId = royaltyRecipientIndexBySellerAndRecipient[_seller.treasury];\n\n                if (royaltyRecipientId != 0) {\n                    RoyaltyRecipientInfo[] storage royaltyRecipients = lookups.royaltyRecipientsBySeller[_seller.id];\n\n                    // If the new treasury is already a royalty recipient, remove it\n                    royaltyRecipientId--; // royaltyRecipientId is 1-based, so we need to decrement it to get the index\n                    uint256 lastRoyaltyRecipientsId = royaltyRecipients.length - 1;\n                    if (royaltyRecipientId != lastRoyaltyRecipientsId) {\n                        royaltyRecipients[royaltyRecipientId] = royaltyRecipients[lastRoyaltyRecipientsId];\n                        royaltyRecipientIndexBySellerAndRecipient[royaltyRecipients[royaltyRecipientId].wallet] =\n                            royaltyRecipientId +\n                            1;\n                    }\n                    royaltyRecipients.pop();\n\n                    delete royaltyRecipientIndexBySellerAndRecipient[_seller.treasury];\n                }\n\n                // Update treasury\n                seller.treasury = _seller.treasury;\n\n                updateApplied = true;\n\n                emit RoyaltyRecipientsChanged(_seller.id, fetchRoyaltyRecipients(_seller.id), msgSender());\n            }\n\n            if (keccak256(bytes(_seller.metadataUri)) != keccak256(bytes(seller.metadataUri))) {\n                // Update metadata URI\n                seller.metadataUri = _seller.metadataUri;\n\n                updateApplied = true;\n            }\n\n            if (updateApplied) {\n                // Notify watchers of state change\n                emit SellerUpdateApplied(\n                    _seller.id,\n                    seller,\n                    sellerPendingUpdate,\n                    authToken,\n                    authTokenPendingUpdate,\n                    msgSender()\n                );\n            }\n\n            if (needsApproval) {\n                // Notify watchers of state change\n                emit SellerUpdatePending(_seller.id, sellerPendingUpdate, authTokenPendingUpdate, msgSender());\n            }\n\n            if (!updateApplied && !needsApproval) revert NoUpdateApplied();\n        }\n    }\n\n    /**\n     * @notice Opt-in to a pending seller update.\n     *\n     * Emits a SellerUpdateApplied event if successful.\n     *\n     * Reverts if:\n     * - The sellers region of protocol is paused\n     * - Addresses are not unique to this seller\n     * - Caller address is not pending update for the field being updated\n     * - Caller is not the owner of the pending AuthToken being updated\n     * - No pending update exists for this seller\n     * - AuthTokenType is not unique to this seller\n     * - Seller tries to update the clerk\n     *\n     * @param _sellerId - seller id\n     * @param _fieldsToUpdate - fields to update, see SellerUpdateFields enum\n     */\n    function optInToSellerUpdate(\n        uint256 _sellerId,\n        SellerUpdateFields[] calldata _fieldsToUpdate\n    ) external sellersNotPaused nonReentrant {\n        Seller storage sellerPendingUpdate;\n        AuthToken storage authTokenPendingUpdate;\n\n        {\n            bool exists;\n            // Get seller pending update\n            (exists, sellerPendingUpdate, authTokenPendingUpdate) = fetchSellerPendingUpdate(_sellerId);\n\n            // Be sure an update is pending\n            if (!exists) revert NoPendingUpdateForAccount();\n        }\n\n        bool updateApplied;\n\n        // Get storage location for seller\n        (, Seller storage seller, AuthToken storage authToken) = fetchSeller(_sellerId);\n\n        // Cache protocol lookups and sender for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        address sender = msgSender();\n\n        for (uint256 i = 0; i < _fieldsToUpdate.length; ) {\n            SellerUpdateFields role = _fieldsToUpdate[i];\n\n            // Approve admin update\n            if (role == SellerUpdateFields.Admin && sellerPendingUpdate.admin != address(0)) {\n                if (sellerPendingUpdate.admin != sender) revert UnauthorizedCallerUpdate();\n\n                preUpdateSellerCheck(_sellerId, sender, lookups);\n\n                // Delete old seller id by admin mapping\n                delete lookups.sellerIdByAdmin[seller.admin];\n\n                // Update admin\n                seller.admin = sender;\n\n                // Store new seller id by admin mapping\n                lookups.sellerIdByAdmin[sender] = _sellerId;\n\n                // Delete pending update admin\n                delete sellerPendingUpdate.admin;\n\n                // Delete auth token for seller id if it exists\n                if (authToken.tokenType != AuthTokenType.None) {\n                    delete lookups.sellerIdByAuthToken[authToken.tokenType][authToken.tokenId];\n                    delete protocolEntities().authTokens[_sellerId];\n                }\n\n                updateApplied = true;\n            } else if (role == SellerUpdateFields.Assistant && sellerPendingUpdate.assistant != address(0)) {\n                // Approve assistant update\n                if (sellerPendingUpdate.assistant != sender) revert UnauthorizedCallerUpdate();\n\n                preUpdateSellerCheck(_sellerId, sender, lookups);\n\n                // Delete old seller id by assistant mapping\n                delete lookups.sellerIdByAssistant[seller.assistant];\n\n                // Update assistant\n                seller.assistant = sender;\n\n                // Transfer ownership of voucher contract to new assistant\n                IBosonVoucher(lookups.cloneAddress[_sellerId]).transferOwnership(sender); // default voucher contract\n                Collection[] storage sellersAdditionalCollections = lookups.additionalCollections[_sellerId];\n                uint256 collectionCount = sellersAdditionalCollections.length;\n                for (uint256 j = 0; j < collectionCount; ) {\n                    // Additional collections (if they exist)\n                    IBosonVoucher(sellersAdditionalCollections[j].collectionAddress).transferOwnership(sender);\n\n                    unchecked {\n                        j++;\n                    }\n                }\n\n                // Store new seller id by assistant mapping\n                lookups.sellerIdByAssistant[sender] = _sellerId;\n\n                // Delete pending update assistant\n                delete sellerPendingUpdate.assistant;\n\n                updateApplied = true;\n            } else if (role == SellerUpdateFields.AuthToken && authTokenPendingUpdate.tokenType != AuthTokenType.None) {\n                // Approve auth token update\n                address authTokenContract = lookups.authTokenContracts[authTokenPendingUpdate.tokenType];\n                address tokenIdOwner = IERC721(authTokenContract).ownerOf(authTokenPendingUpdate.tokenId);\n                if (tokenIdOwner != sender) revert UnauthorizedCallerUpdate();\n\n                // Check that auth token is unique to this seller\n                if (lookups.sellerIdByAuthToken[authTokenPendingUpdate.tokenType][authTokenPendingUpdate.tokenId] != 0)\n                    revert AuthTokenMustBeUnique();\n\n                // Delete old seller id by auth token mapping\n                delete lookups.sellerIdByAuthToken[authToken.tokenType][authToken.tokenId];\n\n                // Update auth token\n                authToken.tokenType = authTokenPendingUpdate.tokenType;\n                authToken.tokenId = authTokenPendingUpdate.tokenId;\n\n                // Store seller by auth token reference\n                lookups.sellerIdByAuthToken[authTokenPendingUpdate.tokenType][\n                    authTokenPendingUpdate.tokenId\n                ] = _sellerId;\n\n                // Remove previous admin address if it exists\n                delete lookups.sellerIdByAdmin[seller.admin];\n                delete seller.admin;\n\n                // Delete pending update auth token\n                delete authTokenPendingUpdate.tokenType;\n                delete authTokenPendingUpdate.tokenId;\n\n                updateApplied = true;\n            } else if (role == SellerUpdateFields.Clerk) {\n                revert ClerkDeprecated();\n            }\n\n            unchecked {\n                i++;\n            }\n        }\n\n        if (updateApplied) {\n            // Notify watchers of state change\n            emit SellerUpdateApplied(\n                _sellerId,\n                seller,\n                sellerPendingUpdate,\n                authToken,\n                authTokenPendingUpdate,\n                msgSender()\n            );\n        }\n    }\n\n    /*\n     * @notice Creates a new seller collection.\n     *\n     * Emits a CollectionCreated event if successful.\n     *\n     * Reverts if:\n     * - The sellers region of protocol is paused\n     * - Caller is not the seller assistant\n     *\n     * @param _externalId - external collection id\n     * @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct\n     */\n    function createNewCollection(\n        string calldata _externalId,\n        VoucherInitValues calldata _voucherInitValues\n    ) external sellersNotPaused nonReentrant {\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n        address assistant = msgSender();\n\n        (bool exists, uint256 sellerId) = getSellerIdByAssistant(assistant);\n        if (!exists) revert NoSuchSeller();\n\n        Collection[] storage sellersAdditionalCollections = lookups.additionalCollections[sellerId];\n        uint256 collectionIndex = sellersAdditionalCollections.length + 1; // 0 is reserved for the original collection\n\n        bytes32 sellerSalt = lookups.sellerSalt[sellerId];\n\n        // Accounts created before v2.3.0 can be missing sellerSalt, so it's created here\n        if (sellerSalt == 0) {\n            (, Seller storage seller, AuthToken storage authToken) = fetchSeller(sellerId);\n            address admin = seller.admin;\n            if (admin == address(0)) {\n                admin = IERC721(lookups.authTokenContracts[authToken.tokenType]).ownerOf(authToken.tokenId);\n            }\n            sellerSalt = keccak256(abi.encodePacked(admin, _voucherInitValues.collectionSalt));\n            if (lookups.isUsedSellerSalt[sellerSalt]) revert SellerSaltNotUnique();\n            lookups.sellerSalt[sellerId] = sellerSalt;\n            lookups.isUsedSellerSalt[sellerSalt] = true;\n        }\n\n        // Create clone and store its address to additionalCollections\n        address voucherCloneAddress = cloneBosonVoucher(\n            sellerId,\n            collectionIndex,\n            sellerSalt,\n            assistant,\n            _voucherInitValues\n        );\n\n        // Store collection details\n        Collection storage newCollection = sellersAdditionalCollections.push();\n        newCollection.collectionAddress = voucherCloneAddress;\n        newCollection.externalId = _externalId;\n\n        emit CollectionCreated(sellerId, collectionIndex, voucherCloneAddress, _externalId, assistant);\n    }\n\n    /**\n     * @notice Updates a salt.\n     * Use this if the admin address is updated and there exists a possibility that the old admin will try to create the vouchers\n     * with matching addresses on other chains.\n     *\n     * Reverts if:\n     * - The sellers region of protocol is paused\n     * - Caller is not the admin of any seller\n     * - Seller salt is not unique\n     *\n     * @param _sellerId - the id of the seller\n     * @param _newSalt - new salt\n     */\n    function updateSellerSalt(uint256 _sellerId, bytes32 _newSalt) external sellersNotPaused nonReentrant {\n        address admin = msgSender();\n\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        (bool exists, Seller storage seller, AuthToken storage authToken) = fetchSeller(_sellerId);\n\n        // Seller must already exist\n        if (!exists) revert NoSuchSeller();\n\n        // Check that caller is authorized to call this function\n        authorizeAdmin(lookups, authToken, seller.admin, admin);\n\n        bytes32 sellerSalt = keccak256(abi.encodePacked(admin, _newSalt));\n\n        if (lookups.isUsedSellerSalt[sellerSalt]) revert SellerSaltNotUnique();\n        lookups.isUsedSellerSalt[lookups.sellerSalt[_sellerId]] = false;\n        lookups.sellerSalt[_sellerId] = sellerSalt;\n        lookups.isUsedSellerSalt[sellerSalt] = true;\n    }\n\n    /**\n     * @notice Adds royalty recipients to a seller.\n     *\n     * Emits a RoyalRecipientsUpdated event if successful.\n     *\n     *  Reverts if:\n     *  - The sellers region of protocol is paused\n     *  - Seller does not exist\n     *  - Caller is not the seller admin\n     *  - Caller does not own auth token\n     *  - Some recipient is not unique\n     *  - some royalty percentage is above the limit\n     *\n     * @param _sellerId - seller id\n     * @param _royaltyRecipients - list of royalty recipients to add, including minimal royalty percentage\n     */\n    function addRoyaltyRecipients(\n        uint256 _sellerId,\n        RoyaltyRecipientInfo[] calldata _royaltyRecipients\n    ) external sellersNotPaused nonReentrant {\n        // Cache protocol lookups and sender for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Make sure admin is the caller and get the sender's address\n        (Seller storage seller, address sender) = validateAdminStatus(lookups, _sellerId);\n        address treasury = seller.treasury;\n\n        RoyaltyRecipientInfo[] storage royaltyRecipients = lookups.royaltyRecipientsBySeller[_sellerId];\n        uint256 maxRoyaltyPercentage = protocolLimits().maxRoyaltyPercentage;\n        uint256 royaltyRecipientsStorageLength = royaltyRecipients.length + 1;\n        for (uint256 i = 0; i < _royaltyRecipients.length; ) {\n            // Cache storage pointer to avoid multiple lookups\n            mapping(address => uint256) storage royaltyRecipientIndexByRecipient = lookups\n                .royaltyRecipientIndexBySellerAndRecipient[_sellerId];\n\n            if (\n                _royaltyRecipients[i].wallet == treasury ||\n                _royaltyRecipients[i].wallet == address(0) ||\n                royaltyRecipientIndexByRecipient[_royaltyRecipients[i].wallet] != 0\n            ) revert RecipientNotUnique();\n\n            if (_royaltyRecipients[i].minRoyaltyPercentage > maxRoyaltyPercentage) revert InvalidRoyaltyPercentage();\n\n            royaltyRecipients.push(_royaltyRecipients[i]);\n            royaltyRecipientIndexByRecipient[_royaltyRecipients[i].wallet] = royaltyRecipientsStorageLength + i;\n\n            createRoyaltyRecipientAccount(_royaltyRecipients[i].wallet);\n\n            unchecked {\n                i++;\n            }\n        }\n\n        emit RoyaltyRecipientsChanged(_sellerId, fetchRoyaltyRecipients(_sellerId), sender);\n    }\n\n    function createRoyaltyRecipientAccount(address payable _royaltyRecipient) internal {\n        mapping(address => uint256) storage royaltyRecipientIdByWallet = protocolLookups().royaltyRecipientIdByWallet;\n        // If account exists, do nothing\n        if (royaltyRecipientIdByWallet[_royaltyRecipient] > 0) {\n            return;\n        }\n\n        uint256 royaltyRecipientId = protocolCounters().nextAccountId++;\n\n        protocolEntities().royaltyRecipients[royaltyRecipientId] = RoyaltyRecipient({\n            id: royaltyRecipientId,\n            wallet: _royaltyRecipient\n        });\n\n        royaltyRecipientIdByWallet[_royaltyRecipient] = royaltyRecipientId;\n    }\n\n    /**\n     * @notice Updates seller's royalty recipients.\n     *\n     * Emits a RoyalRecipientsUpdated event if successful.\n     *\n     *  Reverts if:\n     *  - The sellers region of protocol is paused\n     *  - Seller does not exist\n     *  - Caller is not the seller admin\n     *  - Caller does not own auth token\n     *  - Length of ids to change does not match length of new values\n     *  - Id to update does not exist\n     *  - Seller tries to update the address of default recipient\n     *  - Some recipient is not unique\n     *  - Some royalty percentage is above the limit\n     *\n     * @param _sellerId - seller id\n     * @param _royaltyRecipientIds - list of royalty recipient ids to update. Ids are zero based and corresponds to ids returned by `getRoyaltyRecipients`.\n     * @param _royaltyRecipients - list of new royalty recipients corresponding to ids\n     */\n    function updateRoyaltyRecipients(\n        uint256 _sellerId,\n        uint256[] calldata _royaltyRecipientIds,\n        RoyaltyRecipientInfo[] calldata _royaltyRecipients\n    ) external sellersNotPaused nonReentrant {\n        // Cache protocol lookups and sender for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Make sure admin is the caller and get the seller\n        address treasury;\n        {\n            (Seller storage seller, ) = validateAdminStatus(lookups, _sellerId);\n            treasury = seller.treasury;\n        }\n\n        uint256 royaltyRecipientIdsLength = _royaltyRecipientIds.length;\n        if (royaltyRecipientIdsLength != _royaltyRecipients.length) revert ArrayLengthMismatch();\n\n        RoyaltyRecipientInfo[] storage royaltyRecipients = lookups.royaltyRecipientsBySeller[_sellerId];\n        uint256 royaltyRecipientsLength = royaltyRecipients.length;\n        for (uint256 i = 0; i < royaltyRecipientIdsLength; ) {\n            uint256 royaltyRecipientId = _royaltyRecipientIds[i];\n\n            if (royaltyRecipientId >= royaltyRecipientsLength) revert InvalidRoyaltyRecipientId();\n\n            if (_royaltyRecipients[i].wallet == treasury) revert RecipientNotUnique();\n\n            if (royaltyRecipientId == 0) {\n                if (_royaltyRecipients[i].wallet != address(0)) revert WrongDefaultRecipient();\n            } else {\n                // Cache storage pointer to avoid multiple lookups\n                mapping(address => uint256) storage royaltyRecipientIndexByRecipient = lookups\n                    .royaltyRecipientIndexBySellerAndRecipient[_sellerId];\n\n                uint256 royaltyRecipientIndex = royaltyRecipientIndexByRecipient[_royaltyRecipients[i].wallet];\n\n                if (royaltyRecipientIndex == 0) {\n                    if (_royaltyRecipients[i].wallet == address(0)) revert RecipientNotUnique();\n\n                    // update index\n                    royaltyRecipientIndexByRecipient[_royaltyRecipients[i].wallet] = royaltyRecipientId + 1;\n                    delete royaltyRecipientIndexByRecipient[royaltyRecipients[royaltyRecipientId].wallet];\n                } else {\n                    if (royaltyRecipientIndex - 1 != royaltyRecipientId) revert RecipientNotUnique();\n                }\n            }\n            if (_royaltyRecipients[i].minRoyaltyPercentage > protocolLimits().maxRoyaltyPercentage)\n                revert InvalidRoyaltyPercentage();\n\n            royaltyRecipients[royaltyRecipientId] = _royaltyRecipients[i];\n\n            createRoyaltyRecipientAccount(_royaltyRecipients[i].wallet);\n\n            unchecked {\n                i++;\n            }\n        }\n\n        emit RoyaltyRecipientsChanged(_sellerId, fetchRoyaltyRecipients(_sellerId), msgSender());\n    }\n\n    /**\n     * @notice Removes seller's royalty recipients.\n     *\n     * Emits a RoyalRecipientsUpdated event if successful.\n     *\n     *  Reverts if:\n     *  - The sellers region of protocol is paused\n     *  - Seller does not exist\n     *  - Caller is not the seller admin\n     *  - Caller does not own auth token\n     *  - List of ids to remove is not sorted in ascending order\n     *  - Id to remove does not exist\n     *  - Seller tries to remove the default recipient\n     *\n     * @param _sellerId - seller id\n     * @param _royaltyRecipientIds - list of royalty recipient ids to remove. Ids are zero based and corresponds to ids returned by `getRoyaltyRecipients`.\n     */\n    function removeRoyaltyRecipients(\n        uint256 _sellerId,\n        uint256[] calldata _royaltyRecipientIds\n    ) external sellersNotPaused nonReentrant {\n        // Cache protocol lookups and sender for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Make sure admin is the caller and get the sender's address\n        (, address sender) = validateAdminStatus(lookups, _sellerId);\n\n        RoyaltyRecipientInfo[] storage royaltyRecipients = lookups.royaltyRecipientsBySeller[_sellerId];\n\n        // We loop from the end of the array to ensure correct ids are removed\n        // _royaltyRecipients must be sorted in ascending order\n        uint256 previousId = royaltyRecipients.length; // this is 1 more than the max id. This is used to ensure that royaltyRecipients is sorted in ascending order\n        uint256 lastRoyaltyRecipientsId = previousId - 1; // will never underflow, since at least default recipient always exists\n\n        for (uint256 i = _royaltyRecipientIds.length; i > 0; ) {\n            uint256 royaltyRecipientId = _royaltyRecipientIds[i - 1];\n\n            if (royaltyRecipientId >= previousId) revert RoyaltyRecipientIdsNotSorted(); // this also ensures that royaltyRecipientId will never be out of bounds\n            if (royaltyRecipientId == 0) revert CannotRemoveDefaultRecipient();\n\n            // Cache storage pointer to avoid multiple lookups\n            mapping(address => uint256) storage royaltyRecipientIndexByRecipient = lookups\n                .royaltyRecipientIndexBySellerAndRecipient[_sellerId];\n\n            delete royaltyRecipientIndexByRecipient[royaltyRecipients[royaltyRecipientId].wallet];\n\n            if (royaltyRecipientId != lastRoyaltyRecipientsId) {\n                royaltyRecipients[royaltyRecipientId] = royaltyRecipients[lastRoyaltyRecipientsId];\n                royaltyRecipientIndexByRecipient[royaltyRecipients[royaltyRecipientId].wallet] = royaltyRecipientId;\n            }\n            royaltyRecipients.pop();\n            lastRoyaltyRecipientsId--; // will never underflow. Even if all non-default royalty recipients are removed, default recipient will remain\n\n            // Update previous id\n            previousId = royaltyRecipientId;\n\n            unchecked {\n                i--;\n            }\n        }\n\n        emit RoyaltyRecipientsChanged(_sellerId, fetchRoyaltyRecipients(_sellerId), sender);\n    }\n\n    /**\n     * @notice Gets the details about a seller.\n     *\n     * @param _sellerId - the id of the seller to check\n     * @return exists - the seller was found\n     * @return seller - the seller details. See {BosonTypes.Seller}\n     * @return authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     *                     See {BosonTypes.AuthToken}\n     */\n    function getSeller(\n        uint256 _sellerId\n    ) external view returns (bool exists, Seller memory seller, AuthToken memory authToken) {\n        return fetchSellerWithoutClerk(_sellerId);\n    }\n\n    /**\n     * @notice Gets the details about a seller by an address associated with that seller: assistant or admin address.\n     * A seller will have either an admin address or an auth token.\n     * If seller's admin uses NFT Auth the seller should call `getSellerByAuthToken` instead.\n     *\n     * @param _associatedAddress - the address associated with the seller. Must be an assistant or admin address.\n     * @return exists - the seller was found\n     * @return seller - the seller details. See {BosonTypes.Seller}\n     * @return authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     *                     See {BosonTypes.AuthToken}\n     */\n    function getSellerByAddress(\n        address _associatedAddress\n    ) external view returns (bool exists, Seller memory seller, AuthToken memory authToken) {\n        uint256 sellerId;\n\n        (exists, sellerId) = getSellerIdByAssistant(_associatedAddress);\n        if (exists) {\n            return fetchSellerWithoutClerk(sellerId);\n        }\n\n        (exists, sellerId) = getSellerIdByAdmin(_associatedAddress);\n        if (exists) {\n            return fetchSellerWithoutClerk(sellerId);\n        }\n    }\n\n    /**\n     * @notice Gets the details about a seller by an auth token associated with that seller.\n     * A seller will have either an admin address or an auth token.\n     * If seller's admin uses an admin address, the seller should call `getSellerByAddress` instead.\n     *\n     *\n     * @param _associatedAuthToken - the auth token that may be associated with the seller.\n     * @return exists - the seller was found\n     * @return seller - the seller details. See {BosonTypes.Seller}\n     * @return authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions\n     *                     See {BosonTypes.AuthToken}\n     */\n    function getSellerByAuthToken(\n        AuthToken calldata _associatedAuthToken\n    ) external view returns (bool exists, Seller memory seller, AuthToken memory authToken) {\n        uint256 sellerId;\n\n        (exists, sellerId) = getSellerIdByAuthToken(_associatedAuthToken);\n        if (exists) {\n            return fetchSellerWithoutClerk(sellerId);\n        }\n    }\n\n    /**\n     * @notice Gets the details about all seller's collections.\n     * In case seller has too many collections and this runs out of gas, please use getSellersCollectionsPaginated.\n     *\n     * @param _sellerId - the id of the seller to check\n     * @return defaultVoucherAddress - the address of the default voucher contract for the seller\n     * @return additionalCollections - an array of additional collections that the seller has created\n     */\n    function getSellersCollections(\n        uint256 _sellerId\n    ) external view returns (address defaultVoucherAddress, Collection[] memory additionalCollections) {\n        ProtocolLib.ProtocolLookups storage pl = protocolLookups();\n        return (pl.cloneAddress[_sellerId], pl.additionalCollections[_sellerId]);\n    }\n\n    /**\n     * @notice Gets the details about all seller's collections.\n     * Use getSellersCollectionCount to get the total number of collections.\n     *\n     * @param _sellerId - the id of the seller to check\n     * @param _limit - the maximum number of Collections that should be returned starting from the index defined by `_offset`. If `_offset` + `_limit` exceeds total number of collections, `_limit` is adjusted to return all remaining collections.\n     * @param _offset - the starting index from which to return collections. If `_offset` is greater than or equal to total number of collections, an empty list is returned.\n     * @return defaultVoucherAddress - the address of the default voucher contract for the seller\n     * @return additionalCollections - an array of additional collections that the seller has created\n     */\n    function getSellersCollectionsPaginated(\n        uint256 _sellerId,\n        uint256 _limit,\n        uint256 _offset\n    ) external view returns (address defaultVoucherAddress, Collection[] memory additionalCollections) {\n        ProtocolLib.ProtocolLookups storage pl = protocolLookups();\n        Collection[] storage sellersAdditionalCollections = pl.additionalCollections[_sellerId];\n        uint256 collectionCount = sellersAdditionalCollections.length;\n\n        if (_offset >= collectionCount) {\n            return (pl.cloneAddress[_sellerId], new Collection[](0));\n        } else if (_offset + _limit > collectionCount) {\n            _limit = collectionCount - _offset;\n        }\n\n        additionalCollections = new Collection[](_limit);\n\n        for (uint256 i = 0; i < _limit; ) {\n            additionalCollections[i] = sellersAdditionalCollections[_offset++];\n            unchecked {\n                i++;\n            }\n        }\n\n        return (pl.cloneAddress[_sellerId], additionalCollections);\n    }\n\n    /**\n     * @notice Returns the number of additional collections for a seller.\n     * Use this in conjunction with getSellersCollectionsPaginated to get all collections.\n     *\n     * @param _sellerId - the id of the seller to check\n     */\n    function getSellersCollectionCount(uint256 _sellerId) external view returns (uint256 collectionCount) {\n        return protocolLookups().additionalCollections[_sellerId].length;\n    }\n\n    /**\n     * @notice Returns the availability of salt for a seller.\n     *\n     * @param _adminAddres - the admin address to check\n     * @param _salt - the salt to check (corresponds to `collectionSalt` when `createSeller` or `createNewCollection` is called or `newSalt` when `updateSellerSalt` is called)\n     * @return isAvailable - salt can be used\n     */\n    function isSellerSaltAvailable(address _adminAddres, bytes32 _salt) external view returns (bool isAvailable) {\n        bytes32 sellerSalt = keccak256(abi.encodePacked(_adminAddres, _salt));\n        return !protocolLookups().isUsedSellerSalt[sellerSalt];\n    }\n\n    /**\n     * @notice Calculates the expected collection address and tells if it's still available.\n     *\n     * @param _sellerId - the seller id\n     * @param _collectionSalt - the collection specific salt\n     * @return collectionAddress - the collection address\n     * @return isAvailable - whether the collection address is available\n     */\n    function calculateCollectionAddress(\n        uint256 _sellerId,\n        bytes32 _collectionSalt\n    ) external view returns (address collectionAddress, bool isAvailable) {\n        (bool exist, Seller storage seller, AuthToken storage authToken) = fetchSeller(_sellerId);\n        if (!exist) {\n            return (address(0), false);\n        }\n\n        // get seller salt\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n        bytes32 sellerSalt = lookups.sellerSalt[_sellerId];\n\n        // If seller salt is not set, calculate it\n        if (sellerSalt == 0) {\n            address admin = seller.admin;\n            if (admin == address(0)) {\n                admin = IERC721(lookups.authTokenContracts[authToken.tokenType]).ownerOf(authToken.tokenId);\n            }\n            sellerSalt = keccak256(abi.encodePacked(admin, _collectionSalt));\n        }\n\n        // Calculate collection address\n        bytes32 collectionSalt = keccak256(abi.encodePacked(sellerSalt, _collectionSalt));\n        bytes32 bytecodeHash = keccak256(\n            abi.encodePacked(\n                bytes20(hex\"3d602d80600a3d3981f3363d3d373d3d3d363d73\"),\n                protocolAddresses().beaconProxy,\n                bytes15(0x5af43d82803e903d91602b57fd5bf3)\n            )\n        );\n\n        collectionAddress = Create2.computeAddress(collectionSalt, bytecodeHash, address(this));\n\n        // Check if collection address is available\n        isAvailable = !Address.isContract(collectionAddress);\n    }\n\n    /**\n     * @notice Gets seller's royalty recipients.\n     *\n     * @param _sellerId - seller id\n     * @return royaltyRecipients - list of royalty recipients\n     */\n    function getRoyaltyRecipients(\n        uint256 _sellerId\n    ) external view returns (RoyaltyRecipientInfo[] memory royaltyRecipients) {\n        return fetchRoyaltyRecipients(_sellerId);\n    }\n\n    /**\n     * @notice Pre update Seller checks\n     *\n     * Reverts if:\n     *   - Address has already been used by another seller as assistant or admin\n     *\n     * @param _sellerId - the id of the seller to check\n     * @param _role - the address to check\n     * @param _lookups - the lookups struct\n     */\n    function preUpdateSellerCheck(\n        uint256 _sellerId,\n        address _role,\n        ProtocolLib.ProtocolLookups storage _lookups\n    ) internal view {\n        // Check that the role is unique to one seller id across all roles -- not used or is used by this seller id.\n        if (_role != address(0)) {\n            uint256 check1 = _lookups.sellerIdByAssistant[_role];\n            uint256 check2 = _lookups.sellerIdByAdmin[_role];\n\n            if ((check1 != 0 && check1 != _sellerId) || (check2 != 0 && check2 != _sellerId))\n                revert SellerAddressMustBeUnique();\n        }\n    }\n\n    /**\n     * @notice Fetches a given seller from storage by id and overrides the clerk address with 0x0.\n     *\n     * @param _sellerId - the id of the seller\n     * @return exists - whether the seller exists\n     * @return seller - the seller details. See {BosonTypes.Seller}\n     * @return authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the user can use to do admin functions\n     */\n    function fetchSellerWithoutClerk(\n        uint256 _sellerId\n    ) internal view returns (bool exists, Seller memory seller, AuthToken memory authToken) {\n        (exists, seller, authToken) = fetchSeller(_sellerId);\n        seller.clerk = address(0);\n    }\n\n    /**\n     * @notice Performs a validation that the message sender is either the admin address or the owner of auth token\n     * Reverts if:\n     *   - Seller uses address for authorization and supplied address is not the seller's admin address\n     *   - Seller uses NFT Auth for authorization and supplied address is not the owner of auth NFT\n     *\n     * @param _lookups - the lookups struct\n     * @param _authToken - the auth token to check\n     * @param _admin - the admin address to check\n     * @param _sender - the sender's address to check\n     */\n    function authorizeAdmin(\n        ProtocolLib.ProtocolLookups storage _lookups,\n        AuthToken storage _authToken,\n        address _admin,\n        address _sender\n    ) internal view {\n        if (_admin != address(0)) {\n            if (_admin != _sender) revert NotAdmin();\n        } else {\n            address authTokenContract = _lookups.authTokenContracts[_authToken.tokenType];\n            address tokenIdOwner = IERC721(authTokenContract).ownerOf(_authToken.tokenId);\n            if (tokenIdOwner != _sender) revert NotAdmin();\n        }\n    }\n\n    /**\n     * @notice Gets seller and callers info and validates that the caller is authorized to call a function.\n     *\n     * Reverts if:\n     *   - Seller does not exist\n     *   - Seller uses address for authorization and caller is not the seller's admin address\n     *   - Seller uses NFT Auth for authorization and caller is not the owner of auth NFT\n     *\n     * @param _lookups - the lookups struct\n     * @param _sellerId - the id of the seller to check\n     * @return seller - the seller storage pointer. See {BosonTypes.Seller}\n     * @return sender - the caller's address\n     */\n    function validateAdminStatus(\n        ProtocolLib.ProtocolLookups storage _lookups,\n        uint256 _sellerId\n    ) internal view returns (Seller storage seller, address sender) {\n        // Get message sender\n        sender = msgSender();\n\n        // Check Seller exists in sellers mapping\n        bool exists;\n        AuthToken storage authToken;\n        (exists, seller, authToken) = fetchSeller(_sellerId);\n\n        // Seller must already exist\n        if (!exists) revert NoSuchSeller();\n\n        // Check that caller is authorized to call this function\n        authorizeAdmin(_lookups, authToken, seller.admin, sender);\n    }\n}\n"
      },
      "contracts/protocol/libs/EIP712Lib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport \"../../domain/BosonConstants.sol\";\nimport { BosonErrors } from \"../../domain/BosonErrors.sol\";\nimport { ProtocolLib } from \"../libs/ProtocolLib.sol\";\n\n/**\n * @title EIP712Lib\n *\n * @dev Provides the domain separator and chain id.\n */\nlibrary EIP712Lib {\n    /**\n     * @notice Generates the domain separator hash.\n     * @dev Using the chainId as the salt enables the client to be active on one chain\n     * while a metatx is signed for a contract on another chain. That could happen if the client is,\n     * for instance, a metaverse scene that runs on one chain while the contracts it interacts with are deployed on another chain.\n     *\n     * @param _name - the name of the protocol\n     * @param _version -  The version of the protocol\n     * @return the domain separator hash\n     */\n    function buildDomainSeparator(string memory _name, string memory _version) internal view returns (bytes32) {\n        return\n            keccak256(\n                abi.encode(\n                    EIP712_DOMAIN_TYPEHASH,\n                    keccak256(bytes(_name)),\n                    keccak256(bytes(_version)),\n                    address(this),\n                    block.chainid\n                )\n            );\n    }\n\n    /**\n     * @notice Recovers the Signer from the Signature components.\n     *\n     * Reverts if:\n     * - Signer is the zero address\n     *\n     * @param _user  - the sender of the transaction\n     * @param _hashedMetaTx - hashed meta transaction\n     * @param _sigR - r part of the signer's signature\n     * @param _sigS - s part of the signer's signature\n     * @param _sigV - v part of the signer's signature\n     * @return true if signer is same as _user parameter\n     */\n    function verify(\n        address _user,\n        bytes32 _hashedMetaTx,\n        bytes32 _sigR,\n        bytes32 _sigS,\n        uint8 _sigV\n    ) internal returns (bool) {\n        // Ensure signature is unique\n        // See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/04695aecbd4d17dddfd55de766d10e3805d6f42f/contracts/cryptography/ECDSA.sol#63\n        if (\n            uint256(_sigS) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 ||\n            (_sigV != 27 && _sigV != 28)\n        ) revert BosonErrors.InvalidSignature();\n\n        address signer = ecrecover(toTypedMessageHash(_hashedMetaTx), _sigV, _sigR, _sigS);\n        if (signer == address(0)) revert BosonErrors.InvalidSignature();\n        return signer == _user;\n    }\n\n    /**\n     * @notice Gets the domain separator from storage if matches with the chain id and diamond address, else, build new domain separator.\n     *\n     * @return the domain separator\n     */\n    function getDomainSeparator() private returns (bytes32) {\n        ProtocolLib.ProtocolMetaTxInfo storage pmti = ProtocolLib.protocolMetaTxInfo();\n        uint256 cachedChainId = pmti.cachedChainId;\n\n        if (block.chainid == cachedChainId) {\n            return pmti.domainSeparator;\n        } else {\n            bytes32 domainSeparator = buildDomainSeparator(PROTOCOL_NAME, PROTOCOL_VERSION);\n            pmti.domainSeparator = domainSeparator;\n            pmti.cachedChainId = block.chainid;\n\n            return domainSeparator;\n        }\n    }\n\n    /**\n     * @notice Generates EIP712 compatible message hash.\n     *\n     * @dev Accepts message hash and returns hash message in EIP712 compatible form\n     * so that it can be used to recover signer from signature signed using EIP712 formatted data\n     * https://eips.ethereum.org/EIPS/eip-712\n     * \"\\\\x19\" makes the encoding deterministic\n     * \"\\\\x01\" is the version byte to make it compatible to EIP-191\n     *\n     * @param _messageHash  - the message hash\n     * @return the EIP712 compatible message hash\n     */\n    function toTypedMessageHash(bytes32 _messageHash) internal returns (bytes32) {\n        return keccak256(abi.encodePacked(\"\\x19\\x01\", getDomainSeparator(), _messageHash));\n    }\n\n    /**\n     * @notice Gets the current message sender address from storage.\n     *\n     * @return the the current message sender address from storage\n     */\n    function getCurrentSenderAddress() internal view returns (address) {\n        return ProtocolLib.protocolMetaTxInfo().currentSenderAddress;\n    }\n\n    /**\n     * @notice Returns the message sender address.\n     *\n     * @dev Could be msg.sender or the message sender address from storage (in case of meta transaction).\n     *\n     * @return the message sender address\n     */\n    function msgSender() internal view returns (address) {\n        bool isItAMetaTransaction = ProtocolLib.protocolMetaTxInfo().isMetaTransaction;\n\n        // Get sender from the storage if this is a meta transaction\n        if (isItAMetaTransaction) {\n            address sender = getCurrentSenderAddress();\n            if (sender == address(0)) revert BosonErrors.InvalidAddress();\n\n            return sender;\n        } else {\n            return msg.sender;\n        }\n    }\n}\n"
      },
      "contracts/protocol/libs/ProtocolLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\n\n/**\n * @title ProtocolLib\n *\n * @notice Provides access to the protocol addresses, limits, entities, fees, counters, initializers and  metaTransactions slots for Facets.\n */\nlibrary ProtocolLib {\n    bytes32 internal constant PROTOCOL_ADDRESSES_POSITION = keccak256(\"boson.protocol.addresses\");\n    bytes32 internal constant PROTOCOL_LIMITS_POSITION = keccak256(\"boson.protocol.limits\");\n    bytes32 internal constant PROTOCOL_ENTITIES_POSITION = keccak256(\"boson.protocol.entities\");\n    bytes32 internal constant PROTOCOL_LOOKUPS_POSITION = keccak256(\"boson.protocol.lookups\");\n    bytes32 internal constant PROTOCOL_FEES_POSITION = keccak256(\"boson.protocol.fees\");\n    bytes32 internal constant PROTOCOL_COUNTERS_POSITION = keccak256(\"boson.protocol.counters\");\n    bytes32 internal constant PROTOCOL_STATUS_POSITION = keccak256(\"boson.protocol.initializers\");\n    bytes32 internal constant PROTOCOL_META_TX_POSITION = keccak256(\"boson.protocol.metaTransactions\");\n\n    // Protocol addresses storage\n    struct ProtocolAddresses {\n        // Address of the Boson Protocol treasury\n        address payable treasury;\n        // Address of the Boson Token (ERC-20 contract)\n        address payable token;\n        // Address of the Boson Protocol Voucher beacon\n        address voucherBeacon;\n        // Address of the Boson Beacon proxy implementation\n        address beaconProxy;\n        // Address of the Boson Price Discovery\n        address priceDiscovery;\n    }\n\n    // Protocol limits storage\n    struct ProtocolLimits {\n        // limit on the resolution period that a seller can specify\n        uint256 maxResolutionPeriod;\n        // limit on the escalation response period that a dispute resolver can specify\n        uint256 maxEscalationResponsePeriod;\n        // lower limit for dispute period\n        uint256 minDisputePeriod;\n        // limit how many exchanges can be processed in single batch transaction\n        uint16 maxExchangesPerBatch;\n        // limit how many offers can be added to the group\n        uint16 maxOffersPerGroup;\n        // limit how many offers can be added to the bundle\n        uint16 maxOffersPerBundle;\n        // limit how many twins can be added to the bundle\n        uint16 maxTwinsPerBundle;\n        // limit how many offers can be processed in single batch transaction\n        uint16 maxOffersPerBatch;\n        // limit how many different tokens can be withdrawn in a single transaction\n        uint16 maxTokensPerWithdrawal;\n        // limit how many dispute resolver fee structs can be processed in a single transaction\n        uint16 maxFeesPerDisputeResolver;\n        // limit how many disputes can be processed in single batch transaction\n        uint16 maxDisputesPerBatch;\n        // limit how many sellers can be added to or removed from an allow list in a single transaction\n        uint16 maxAllowedSellers;\n        // limit the sum of (protocol fee percentage + agent fee percentage) of an offer fee\n        uint16 maxTotalOfferFeePercentage;\n        // limit the max royalty percentage that can be set by the seller\n        uint16 maxRoyaltyPercentage;\n        // limit the max number of vouchers that can be preminted in a single transaction\n        uint256 maxPremintedVouchers;\n        // lower limit for resolution period\n        uint256 minResolutionPeriod;\n    }\n\n    // Protocol fees storage\n    struct ProtocolFees {\n        // Percentage that will be taken as a fee from the net of a Boson Protocol exchange\n        uint256 percentage; // 1.75% = 175, 100% = 10000\n        // Flat fee taken for exchanges in $BOSON\n        uint256 flatBoson;\n        // buyer escalation deposit percentage\n        uint256 buyerEscalationDepositPercentage;\n    }\n\n    // Protocol entities storage\n    struct ProtocolEntities {\n        // offer id => offer\n        mapping(uint256 => BosonTypes.Offer) offers;\n        // offer id => offer dates\n        mapping(uint256 => BosonTypes.OfferDates) offerDates;\n        // offer id => offer fees\n        mapping(uint256 => BosonTypes.OfferFees) offerFees;\n        // offer id => offer durations\n        mapping(uint256 => BosonTypes.OfferDurations) offerDurations;\n        // offer id => dispute resolution terms\n        mapping(uint256 => BosonTypes.DisputeResolutionTerms) disputeResolutionTerms;\n        // exchange id => exchange\n        mapping(uint256 => BosonTypes.Exchange) exchanges;\n        // exchange id => voucher\n        mapping(uint256 => BosonTypes.Voucher) vouchers;\n        // exchange id => dispute\n        mapping(uint256 => BosonTypes.Dispute) disputes;\n        // exchange id => dispute dates\n        mapping(uint256 => BosonTypes.DisputeDates) disputeDates;\n        // seller id => seller\n        mapping(uint256 => BosonTypes.Seller) sellers;\n        // buyer id => buyer\n        mapping(uint256 => BosonTypes.Buyer) buyers;\n        // dispute resolver id => dispute resolver\n        mapping(uint256 => BosonTypes.DisputeResolver) disputeResolvers;\n        // dispute resolver id => dispute resolver fee array\n        mapping(uint256 => BosonTypes.DisputeResolverFee[]) disputeResolverFees;\n        // agent id => agent\n        mapping(uint256 => BosonTypes.Agent) agents;\n        // group id => group\n        mapping(uint256 => BosonTypes.Group) groups;\n        // group id => condition\n        mapping(uint256 => BosonTypes.Condition) conditions;\n        // bundle id => bundle\n        mapping(uint256 => BosonTypes.Bundle) bundles;\n        // twin id => twin\n        mapping(uint256 => BosonTypes.Twin) twins;\n        // entity id => auth token\n        mapping(uint256 => BosonTypes.AuthToken) authTokens;\n        // exchange id => sequential commit info\n        mapping(uint256 => BosonTypes.ExchangeCosts[]) exchangeCosts;\n        // entity id => royalty recipient account\n        mapping(uint256 => BosonTypes.RoyaltyRecipient) royaltyRecipients;\n    }\n\n    // Protocol lookups storage\n    struct ProtocolLookups {\n        // offer id => exchange ids\n        mapping(uint256 => uint256[]) exchangeIdsByOffer;\n        // offer id => bundle id\n        mapping(uint256 => uint256) bundleIdByOffer;\n        // twin id => bundle id\n        mapping(uint256 => uint256) bundleIdByTwin;\n        // offer id => group id\n        mapping(uint256 => uint256) groupIdByOffer;\n        // offer id => agent id\n        mapping(uint256 => uint256) agentIdByOffer;\n        // seller assistant address => sellerId\n        mapping(address => uint256) sellerIdByAssistant;\n        // seller admin address => sellerId\n        mapping(address => uint256) sellerIdByAdmin;\n        // seller clerk address => sellerId\n        // @deprecated sellerIdByClerk is no longer used. Keeping it for backwards compatibility.\n        mapping(address => uint256) sellerIdByClerk;\n        // buyer wallet address => buyerId\n        mapping(address => uint256) buyerIdByWallet;\n        // dispute resolver assistant address => disputeResolverId\n        mapping(address => uint256) disputeResolverIdByAssistant;\n        // dispute resolver admin address => disputeResolverId\n        mapping(address => uint256) disputeResolverIdByAdmin;\n        // dispute resolver clerk address => disputeResolverId\n        // @deprecated disputeResolverIdByClerk is no longer used. Keeping it for backwards compatibility.\n        mapping(address => uint256) disputeResolverIdByClerk;\n        // dispute resolver id to fee token address => index of the token address\n        mapping(uint256 => mapping(address => uint256)) disputeResolverFeeTokenIndex;\n        // agent wallet address => agentId\n        mapping(address => uint256) agentIdByWallet;\n        // account id => token address => amount\n        mapping(uint256 => mapping(address => uint256)) availableFunds;\n        // account id => all tokens with balance > 0\n        mapping(uint256 => address[]) tokenList;\n        // account id => token address => index on token addresses list\n        mapping(uint256 => mapping(address => uint256)) tokenIndexByAccount;\n        // seller id => cloneAddress\n        mapping(uint256 => address) cloneAddress;\n        // buyer id => number of active vouchers\n        mapping(uint256 => uint256) voucherCount;\n        // buyer address => groupId => commit count (addresses that have committed to conditional offers)\n        mapping(address => mapping(uint256 => uint256)) conditionalCommitsByAddress;\n        // AuthTokenType => Auth NFT contract address.\n        mapping(BosonTypes.AuthTokenType => address) authTokenContracts;\n        // AuthTokenType => tokenId => sellerId\n        mapping(BosonTypes.AuthTokenType => mapping(uint256 => uint256)) sellerIdByAuthToken;\n        // seller id => token address (only ERC721) => start and end of token ids range\n        mapping(uint256 => mapping(address => BosonTypes.TokenRange[])) twinRangesBySeller;\n        // seller id => token address (only ERC721) => twin ids\n        // @deprecated twinIdsByTokenAddressAndBySeller is no longer used. Keeping it for backwards compatibility.\n        mapping(uint256 => mapping(address => uint256[])) twinIdsByTokenAddressAndBySeller;\n        // exchange id => BosonTypes.TwinReceipt\n        mapping(uint256 => BosonTypes.TwinReceipt[]) twinReceiptsByExchange;\n        // dispute resolver id => list of allowed sellers\n        mapping(uint256 => uint256[]) allowedSellers;\n        // dispute resolver id => seller id => index of allowed seller in allowedSellers\n        mapping(uint256 => mapping(uint256 => uint256)) allowedSellerIndex;\n        // exchange id => condition\n        mapping(uint256 => BosonTypes.Condition) exchangeCondition;\n        // groupId => offerId => index on Group.offerIds array\n        mapping(uint256 => mapping(uint256 => uint256)) offerIdIndexByGroup;\n        // seller id => Seller\n        mapping(uint256 => BosonTypes.Seller) pendingAddressUpdatesBySeller;\n        // seller id => AuthToken\n        mapping(uint256 => BosonTypes.AuthToken) pendingAuthTokenUpdatesBySeller;\n        // dispute resolver id => DisputeResolver\n        mapping(uint256 => BosonTypes.DisputeResolver) pendingAddressUpdatesByDisputeResolver;\n        // twin id => range id\n        mapping(uint256 => uint256) rangeIdByTwin;\n        // tokenId => groupId =>  commit count (count how many times a token has been used as gate for this group)\n        mapping(uint256 => mapping(uint256 => uint256)) conditionalCommitsByTokenId;\n        // seller id => collections\n        mapping(uint256 => BosonTypes.Collection[]) additionalCollections;\n        // seller id => seller salt used to create collections\n        mapping(uint256 => bytes32) sellerSalt;\n        // seller salt => is used\n        mapping(bytes32 => bool) isUsedSellerSalt;\n        // seller id => royalty recipients info\n        mapping(uint256 => BosonTypes.RoyaltyRecipientInfo[]) royaltyRecipientsBySeller;\n        // seller id => royalty recipient => index of royalty recipient in royaltyRecipientsBySeller\n        mapping(uint256 => mapping(address => uint256)) royaltyRecipientIndexBySellerAndRecipient;\n        // royalty recipient wallet address => agentId\n        mapping(address => uint256) royaltyRecipientIdByWallet;\n    }\n\n    // Incrementing id counters\n    struct ProtocolCounters {\n        // Next account id\n        uint256 nextAccountId;\n        // Next offer id\n        uint256 nextOfferId;\n        // Next exchange id\n        uint256 nextExchangeId;\n        // Next twin id\n        uint256 nextTwinId;\n        // Next group id\n        uint256 nextGroupId;\n        // Next twin id\n        uint256 nextBundleId;\n    }\n\n    // Storage related to Meta Transactions\n    struct ProtocolMetaTxInfo {\n        // The current sender address associated with the transaction\n        address currentSenderAddress;\n        // A flag that tells us whether the current transaction is a meta-transaction or a regular transaction.\n        bool isMetaTransaction;\n        // The domain Separator of the protocol\n        bytes32 domainSeparator;\n        // address => nonce => nonce used indicator\n        mapping(address => mapping(uint256 => bool)) usedNonce;\n        // The cached chain id\n        uint256 cachedChainId;\n        // map function name to input type\n        mapping(string => BosonTypes.MetaTxInputType) inputType;\n        // map input type => hash info\n        mapping(BosonTypes.MetaTxInputType => BosonTypes.HashInfo) hashInfo;\n        // Can function be executed using meta transactions\n        mapping(bytes32 => bool) isAllowlisted;\n    }\n\n    // Individual facet initialization states\n    struct ProtocolStatus {\n        // the current pause scenario, a sum of PausableRegions as powers of two\n        uint256 pauseScenario;\n        // reentrancy status\n        uint256 reentrancyStatus;\n        // interface id => initialized?\n        mapping(bytes4 => bool) initializedInterfaces;\n        // version => initialized?\n        mapping(bytes32 => bool) initializedVersions;\n        // Current protocol version\n        bytes32 version;\n        // Incoming voucher id\n        uint256 incomingVoucherId;\n        // Incoming voucher clone address\n        address incomingVoucherCloneAddress;\n    }\n\n    /**\n     * @dev Gets the protocol addresses slot\n     *\n     * @return pa - the protocol addresses slot\n     */\n    function protocolAddresses() internal pure returns (ProtocolAddresses storage pa) {\n        bytes32 position = PROTOCOL_ADDRESSES_POSITION;\n        assembly {\n            pa.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol limits slot\n     *\n     * @return pl - the protocol limits slot\n     */\n    function protocolLimits() internal pure returns (ProtocolLimits storage pl) {\n        bytes32 position = PROTOCOL_LIMITS_POSITION;\n        assembly {\n            pl.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol entities slot\n     *\n     * @return pe - the protocol entities slot\n     */\n    function protocolEntities() internal pure returns (ProtocolEntities storage pe) {\n        bytes32 position = PROTOCOL_ENTITIES_POSITION;\n        assembly {\n            pe.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol lookups slot\n     *\n     * @return pl - the protocol lookups slot\n     */\n    function protocolLookups() internal pure returns (ProtocolLookups storage pl) {\n        bytes32 position = PROTOCOL_LOOKUPS_POSITION;\n        assembly {\n            pl.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol fees slot\n     *\n     * @return pf - the protocol fees slot\n     */\n    function protocolFees() internal pure returns (ProtocolFees storage pf) {\n        bytes32 position = PROTOCOL_FEES_POSITION;\n        assembly {\n            pf.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol counters slot\n     *\n     * @return pc - the protocol counters slot\n     */\n    function protocolCounters() internal pure returns (ProtocolCounters storage pc) {\n        bytes32 position = PROTOCOL_COUNTERS_POSITION;\n        assembly {\n            pc.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol meta-transactions storage slot\n     *\n     * @return pmti - the protocol meta-transactions storage slot\n     */\n    function protocolMetaTxInfo() internal pure returns (ProtocolMetaTxInfo storage pmti) {\n        bytes32 position = PROTOCOL_META_TX_POSITION;\n        assembly {\n            pmti.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol status slot\n     *\n     * @return ps - the the protocol status slot\n     */\n    function protocolStatus() internal pure returns (ProtocolStatus storage ps) {\n        bytes32 position = PROTOCOL_STATUS_POSITION;\n        assembly {\n            ps.slot := position\n        }\n    }\n}\n"
      }
    },
    "settings": {
      "viaIR": false,
      "optimizer": {
        "enabled": true,
        "runs": 200,
        "details": {
          "yul": true
        }
      },
      "evmVersion": "london",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}