creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(IERC20 token, address spender, uint256 value) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n     * Revert on invalid signature.\n     */\n    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return\n            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));\n    }\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/math/Math.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1, \"Math: mulDiv overflow\");\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);\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/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/IBosonDisputeEvents.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 IBosonDisputeEvents\n *\n * @notice Defines events related to disputes within the protocol.\n */\ninterface IBosonDisputeEvents {\n    event DisputeRaised(\n        uint256 indexed exchangeId,\n        uint256 indexed buyerId,\n        uint256 indexed sellerId,\n        address executedBy\n    );\n    event DisputeRetracted(uint256 indexed exchangeId, address indexed executedBy);\n    event DisputeResolved(uint256 indexed exchangeId, uint256 _buyerPercent, address indexed executedBy);\n    event DisputeExpired(uint256 indexed exchangeId, address indexed executedBy);\n    event DisputeDecided(uint256 indexed exchangeId, uint256 _buyerPercent, address indexed executedBy);\n    event DisputeTimeoutExtended(uint256 indexed exchangeId, uint256 newDisputeTimeout, address indexed executedBy);\n    event DisputeEscalated(uint256 indexed exchangeId, uint256 indexed disputeResolverId, address indexed executedBy);\n    event EscalatedDisputeExpired(uint256 indexed exchangeId, address indexed executedBy);\n    event EscalatedDisputeRefused(uint256 indexed exchangeId, address indexed executedBy);\n}\n"
      },
      "contracts/interfaces/events/IBosonFundsEvents.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 IBosonFundsEvents\n *\n * @notice Defines events related to management of funds within the protocol.\n */\ninterface IBosonFundsEvents {\n    event FundsDeposited(\n        uint256 indexed sellerId,\n        address indexed executedBy,\n        address indexed tokenAddress,\n        uint256 amount\n    );\n}\n\ninterface IBosonFundsLibEvents {\n    event FundsEncumbered(\n        uint256 indexed entityId,\n        address indexed exchangeToken,\n        uint256 amount,\n        address indexed executedBy\n    );\n    event FundsReleased(\n        uint256 indexed exchangeId,\n        uint256 indexed entityId,\n        address indexed exchangeToken,\n        uint256 amount,\n        address executedBy\n    );\n    event ProtocolFeeCollected(\n        uint256 indexed exchangeId,\n        address indexed exchangeToken,\n        uint256 amount,\n        address indexed executedBy\n    );\n    event FundsWithdrawn(\n        uint256 indexed sellerId,\n        address indexed withdrawnTo,\n        address indexed tokenAddress,\n        uint256 amount,\n        address executedBy\n    );\n}\n"
      },
      "contracts/interfaces/handlers/IBosonDisputeHandler.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\nimport { BosonErrors } from \"../../domain/BosonErrors.sol\";\nimport { IBosonDisputeEvents } from \"../events/IBosonDisputeEvents.sol\";\nimport { IBosonFundsLibEvents } from \"../events/IBosonFundsEvents.sol\";\n\n/**\n * @title IBosonDisputeHandler\n *\n * @notice Handles disputes associated with exchanges within the protocol.\n *\n * The ERC-165 identifier for this interface is: 0xd9ea8317\n */\ninterface IBosonDisputeHandler is BosonErrors, IBosonDisputeEvents, IBosonFundsLibEvents {\n    /**\n     * @notice Raises a dispute.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Caller does not hold a voucher for the given exchange id\n     * - Exchange does not exist\n     * - Exchange is not in a Redeemed state\n     * - Dispute period has elapsed already\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function raiseDispute(uint256 _exchangeId) external;\n\n    /**\n     * @notice Retracts the dispute and release the funds.\n     *\n     * Emits a DisputeRetracted event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Caller is not the buyer for the given exchange id\n     * - Dispute is in some state other than Resolving or Escalated\n     * - Dispute was escalated and escalation period has elapsed\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function retractDispute(uint256 _exchangeId) external;\n\n    /**\n     * @notice Extends the dispute timeout, allowing more time for mutual resolution.\n     * As a consequence, buyer also gets more time to escalate the dispute.\n     *\n     * Emits a DisputeTimeoutExtened event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Caller is not the seller\n     * - Dispute has expired already\n     * - New dispute timeout is before the current dispute timeout\n     * - Dispute is in some state other than Resolving\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @param _newDisputeTimeout - new date when resolution period ends\n     */\n    function extendDisputeTimeout(uint256 _exchangeId, uint256 _newDisputeTimeout) external;\n\n    /**\n     * @notice Expires the dispute and releases the funds.\n     *\n     * Emits a DisputeExpired event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Dispute is still valid\n     * - Dispute is in some state other than Resolving\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function expireDispute(uint256 _exchangeId) external;\n\n    /**\n     * @notice Expires a batch of disputes and releases the funds.\n     *\n     * Emits a DisputeExpired event for every dispute if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - For any dispute:\n     *   - Exchange does not exist\n     *   - Exchange is not in a Disputed state\n     *   - Dispute is still valid\n     *   - Dispute is in some state other than Resolving\n     *\n     * @param _exchangeIds - the array of ids of the associated exchanges\n     */\n    function expireDisputeBatch(uint256[] calldata _exchangeIds) external;\n\n    /**\n     * @notice Resolves a dispute by providing the information about the funds split.\n     * Callable by the buyer or seller, but the caller must provide the resolution signed by the other party.\n     *\n     * Emits a DisputeResolved event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Specified buyer percent exceeds 100%\n     * - Dispute has expired (resolution period has ended and dispute was not escalated)\n     * - Exchange does not exist\n     * - Exchange is not in the Disputed state\n     * - Caller is neither the seller nor the buyer\n     * - Signature does not belong to the address of the other party\n     * - Dispute state is neither Resolving nor escalated\n     * - Dispute was escalated and escalation period has elapsed\n     *\n     * @param _exchangeId  - the id of the associated exchange\n     * @param _buyerPercent - percentage of the pot that goes to the buyer\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     */\n    function resolveDispute(\n        uint256 _exchangeId,\n        uint256 _buyerPercent,\n        bytes32 _sigR,\n        bytes32 _sigS,\n        uint8 _sigV\n    ) external;\n\n    /**\n     * @notice Puts the dispute into the Escalated state.\n     *\n     * Caller must send (or for ERC20, approve the transfer of) the\n     * buyer escalation deposit percentage of the offer price, which\n     * will be added to the pot for resolution.\n     *\n     * Emits a DisputeEscalated event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Caller is not the buyer\n     * - Dispute is already expired\n     * - Dispute is not in a Resolving state\n     * - Dispute resolver is not specified (absolute zero offer)\n     * - Offer price is in native token and caller does not send enough\n     * - Offer price is in some ERC20 token and caller also sends native currency\n     * - If contract at token address does not support ERC20 function transferFrom\n     * - If calling transferFrom on token fails for some reason (e.g. protocol is not approved to transfer)\n     * - Received ERC20 token amount differs from the expected value\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function escalateDispute(uint256 _exchangeId) external payable;\n\n    /**\n     * @notice Decides a dispute by providing the information about the funds split. Callable by the dispute resolver specified in the offer.\n     *\n     * Emits a DisputeDecided event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Specified buyer percent exceeds 100%\n     * - Exchange does not exist\n     * - Exchange is not in the Disputed state\n     * - Caller is not the dispute resolver for this dispute\n     * - Dispute state is not Escalated\n     * - Dispute escalation response period has elapsed\n     *\n     * @param _exchangeId  - the id of the associated exchange\n     * @param _buyerPercent - percentage of the pot that goes to the buyer\n     */\n    function decideDispute(uint256 _exchangeId, uint256 _buyerPercent) external;\n\n    /**\n     * @notice Enables dispute resolver to explicitly refuse to resolve a dispute in Escalated state and releases the funds.\n     *\n     * Emits an EscalatedDisputeRefused event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Dispute is in some state other than Escalated\n     * - Dispute escalation response period has elapsed\n     * - Caller is not the dispute resolver for this dispute\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function refuseEscalatedDispute(uint256 _exchangeId) external;\n\n    /**\n     * @notice Expires the dispute in escalated state and release the funds.\n     *\n     * Emits an EscalatedDisputeExpired event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Dispute is in some state other than Escalated\n     * - Dispute escalation period has not passed yet\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function expireEscalatedDispute(uint256 _exchangeId) external;\n\n    /**\n     * @notice Gets the details about a given dispute.\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @return exists - true if the dispute exists\n     * @return dispute - the dispute details. See {BosonTypes.Dispute}\n     * @return disputeDates - the dispute dates details {BosonTypes.DisputeDates}\n     */\n    function getDispute(\n        uint256 _exchangeId\n    )\n        external\n        view\n        returns (bool exists, BosonTypes.Dispute memory dispute, BosonTypes.DisputeDates memory disputeDates);\n\n    /**\n     * @notice Gets the state of a given dispute.\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @return exists - true if the dispute exists\n     * @return state - the dispute state. See {BosonTypes.DisputeState}\n     */\n    function getDisputeState(uint256 _exchangeId) external view returns (bool exists, BosonTypes.DisputeState state);\n\n    /**\n     * @notice Gets the timeout of a given dispute.\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @return exists - true if the dispute exists\n     * @return timeout - the end of resolution period\n     */\n    function getDisputeTimeout(uint256 _exchangeId) external view returns (bool exists, uint256 timeout);\n\n    /**\n     * @notice Checks if the given dispute is in a Finalized state.\n     *\n     * Returns true if\n     * - Dispute state is Retracted, Resolved, Decided or Refused\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @return exists - true if the dispute exists\n     * @return isFinalized - true if the dispute is finalized\n     */\n    function isDisputeFinalized(uint256 _exchangeId) external view returns (bool exists, bool isFinalized);\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/protocol/bases/DisputeBase.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport { IBosonDisputeEvents } from \"../../interfaces/events/IBosonDisputeEvents.sol\";\nimport { IBosonFundsLibEvents } from \"../../interfaces/events/IBosonFundsEvents.sol\";\nimport { ProtocolBase } from \"./../bases/ProtocolBase.sol\";\nimport { ProtocolLib } from \"./../libs/ProtocolLib.sol\";\nimport { FundsLib } from \"./../libs/FundsLib.sol\";\nimport \"../../domain/BosonConstants.sol\";\n\n/**\n *\n * @title DisputeBase\n * @notice Provides methods for dispute that can be shared across facets.\n */\ncontract DisputeBase is ProtocolBase, IBosonDisputeEvents, IBosonFundsLibEvents {\n    /**\n     * @notice Raises a dispute\n     *\n     * Reverts if:\n     * - Caller does not hold a voucher for the given exchange id\n     * - Exchange does not exist\n     * - Dispute period has elapsed already\n     *\n     * @param _exchange - the exchange\n     * @param _voucher - the associated voucher\n     * @param _sellerId - the seller id\n     */\n    function raiseDisputeInternal(Exchange storage _exchange, Voucher storage _voucher, uint256 _sellerId) internal {\n        // Fetch offer durations\n        OfferDurations storage offerDurations = fetchOfferDurations(_exchange.offerId);\n\n        // Make sure the dispute period has not elapsed\n        uint256 elapsed = block.timestamp - _voucher.redeemedDate;\n        if (elapsed >= offerDurations.disputePeriod) revert DisputePeriodHasElapsed();\n\n        // Make sure the caller is buyer associated with the exchange\n        checkBuyer(_exchange.buyerId);\n\n        // Set the exchange state to disputed\n        _exchange.state = ExchangeState.Disputed;\n\n        // Fetch the dispute and dispute dates\n        (, Dispute storage dispute, DisputeDates storage disputeDates) = fetchDispute(_exchange.id);\n\n        // Set the initial values\n        dispute.exchangeId = _exchange.id;\n        dispute.state = DisputeState.Resolving;\n\n        // Update the disputeDates\n        disputeDates.disputed = block.timestamp;\n        disputeDates.timeout = block.timestamp + offerDurations.resolutionPeriod;\n\n        // Notify watchers of state change\n        emit DisputeRaised(_exchange.id, _exchange.buyerId, _sellerId, msgSender());\n    }\n\n    /**\n     * @notice Puts the dispute into the Escalated state.\n     *\n     * Caller must send (or for ERC20, approve the transfer of) the\n     * buyer escalation deposit percentage of the offer price, which\n     * will be added to the pot for resolution.\n     *\n     * Emits a DisputeEscalated event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Caller is not the buyer\n     * - Dispute is already expired\n     * - Dispute is not in a Resolving state\n     * - Dispute resolver is not specified (absolute zero offer)\n     * - Offer price is in native token and caller does not send enough\n     * - Offer price is in some ERC20 token and caller also sends native currency\n     * - If contract at token address does not support ERC20 function transferFrom\n     * - If calling transferFrom on token fails for some reason (e.g. protocol is not approved to transfer)\n     * - Received ERC20 token amount differs from the expected value\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function escalateDisputeInternal(uint256 _exchangeId) internal disputesNotPaused {\n        // Get the exchange, should be in disputed state\n        (Exchange storage exchange, ) = getValidExchange(_exchangeId, ExchangeState.Disputed);\n\n        // Make sure the caller is buyer associated with the exchange\n        uint256 buyerId = exchange.buyerId;\n        checkBuyer(buyerId);\n\n        // Fetch the dispute and dispute dates\n        (, Dispute storage dispute, DisputeDates storage disputeDates) = fetchDispute(_exchangeId);\n\n        // make sure the dispute not expired already\n        if (block.timestamp > disputeDates.timeout) revert DisputeHasExpired();\n\n        // Make sure the dispute is in the resolving state\n        if (dispute.state != DisputeState.Resolving) revert InvalidState();\n\n        // Fetch the dispute resolution terms from the storage\n        DisputeResolutionTerms storage disputeResolutionTerms = fetchDisputeResolutionTerms(exchange.offerId);\n\n        // absolute zero offers can be without DR. In that case we prevent escalation\n        if (disputeResolutionTerms.disputeResolverId == 0) revert EscalationNotAllowed();\n\n        // fetch offer to get info about dispute resolver id\n        (, Offer storage offer) = fetchOffer(exchange.offerId);\n\n        // make sure buyer sent enough funds to proceed\n        address exchangeToken = offer.exchangeToken;\n        uint256 buyerEscalationDeposit = disputeResolutionTerms.buyerEscalationDeposit;\n        FundsLib.validateIncomingPayment(exchangeToken, buyerEscalationDeposit);\n\n        // fetch the escalation period from the storage\n        uint256 escalationResponsePeriod = disputeResolutionTerms.escalationResponsePeriod;\n\n        // store the time of escalation\n        disputeDates.escalated = block.timestamp;\n        disputeDates.timeout = block.timestamp + escalationResponsePeriod;\n\n        // Set the dispute state\n        dispute.state = DisputeState.Escalated;\n\n        // Notify watchers of state change\n        address sender = msgSender();\n        emit FundsEncumbered(buyerId, exchangeToken, buyerEscalationDeposit, sender);\n        emit DisputeEscalated(_exchangeId, disputeResolutionTerms.disputeResolverId, sender);\n    }\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/facets/DisputeHandlerFacet.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport \"../../domain/BosonConstants.sol\";\nimport { IBosonDisputeHandler } from \"../../interfaces/handlers/IBosonDisputeHandler.sol\";\nimport { DiamondLib } from \"../../diamond/DiamondLib.sol\";\nimport { DisputeBase } from \"../bases/DisputeBase.sol\";\nimport { FundsLib } from \"../libs/FundsLib.sol\";\nimport { EIP712Lib } from \"../libs/EIP712Lib.sol\";\n\n/**\n * @title DisputeHandlerFacet\n *\n * @notice Handles disputes associated with exchanges within the protocol.\n */\ncontract DisputeHandlerFacet is DisputeBase, IBosonDisputeHandler {\n    bytes32 private constant RESOLUTION_TYPEHASH =\n        keccak256(bytes(\"Resolution(uint256 exchangeId,uint256 buyerPercentBasisPoints)\")); // needed for verification during the resolveDispute\n\n    /**\n     * @notice Initializes Facet.\n     * This function is callable only once.\n     */\n    function initialize() public onlyUninitialized(type(IBosonDisputeHandler).interfaceId) {\n        DiamondLib.addSupportedInterface(type(IBosonDisputeHandler).interfaceId);\n    }\n\n    /**\n     * @notice Raises a dispute.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Caller does not hold a voucher for the given exchange id\n     * - Exchange does not exist\n     * - Exchange is not in a Redeemed state\n     * - Dispute period has elapsed already\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function raiseDispute(uint256 _exchangeId) external override disputesNotPaused nonReentrant {\n        // Get the exchange, should be in redeemed state\n        (Exchange storage exchange, Voucher storage voucher) = getValidExchange(_exchangeId, ExchangeState.Redeemed);\n\n        // Get the offer, which will exist if the exchange does\n        (, Offer storage offer) = fetchOffer(exchange.offerId);\n\n        // Raise the dispute\n        raiseDisputeInternal(exchange, voucher, offer.sellerId);\n    }\n\n    /**\n     * @notice Retracts the dispute and release the funds.\n     *\n     * Emits a DisputeRetracted event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Caller is not the buyer for the given exchange id\n     * - Dispute is in some state other than Resolving or Escalated\n     * - Dispute was escalated and escalation period has elapsed\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function retractDispute(uint256 _exchangeId) external override nonReentrant {\n        // Get the exchange, should be in disputed state\n        (Exchange storage exchange, ) = getValidExchange(_exchangeId, ExchangeState.Disputed);\n\n        // Make sure the caller is buyer associated with the exchange  // {MR: only by game}\n        checkBuyer(exchange.buyerId);\n\n        // Fetch the dispute\n        (, Dispute storage dispute, DisputeDates storage disputeDates) = fetchDispute(_exchangeId);\n\n        // If dispute was escalated, make sure that escalation period is not over yet\n        if (dispute.state == DisputeState.Escalated) {\n            // make sure the dispute escalation period not expired already\n            if (block.timestamp > disputeDates.timeout) revert DisputeHasExpired();\n        } else {\n            // If dispute is not escalated, make sure the it is in the resolving state\n            if (dispute.state != DisputeState.Resolving) revert InvalidState();\n        }\n\n        // Finalize the dispute\n        finalizeDispute(_exchangeId, exchange, dispute, disputeDates, DisputeState.Retracted, 0);\n\n        // Notify watchers of state change\n        emit DisputeRetracted(_exchangeId, msgSender());\n    }\n\n    /**\n     * @notice Extends the dispute timeout, allowing more time for mutual resolution.\n     * As a consequence, buyer also gets more time to escalate the dispute.\n     *\n     * Emits a DisputeTimeoutExtened event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Caller is not the seller\n     * - Dispute has expired already\n     * - New dispute timeout is before the current dispute timeout\n     * - Dispute is in some state other than Resolving\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @param _newDisputeTimeout - new date when resolution period ends\n     */\n    function extendDisputeTimeout(\n        uint256 _exchangeId,\n        uint256 _newDisputeTimeout\n    ) external override disputesNotPaused nonReentrant {\n        // Verify that the caller is the seller. Get exchange -> get offer id -> get seller id -> get assistant address and compare to msg.sender\n        // Get the exchange, should be in disputed state\n        (Exchange storage exchange, ) = getValidExchange(_exchangeId, ExchangeState.Disputed);\n\n        // Get the offer, assume it exist if exchange exist\n        (, Offer storage offer) = fetchOffer(exchange.offerId);\n\n        // Get seller, we assume seller exists if offer exists\n        (, Seller storage seller, ) = fetchSeller(offer.sellerId);\n\n        // get message sender\n        address sender = msgSender();\n\n        // Caller must be seller's assistant address\n        if (seller.assistant != sender) revert NotAssistant();\n\n        // Fetch the dispute, it exists if exchange is in Disputed state\n        (, Dispute storage dispute, DisputeDates storage disputeDates) = fetchDispute(_exchangeId);\n\n        // Dispute must be in a resolving state\n        if (dispute.state != DisputeState.Resolving) revert InvalidState();\n\n        // If expired already, it cannot be extended\n        if (block.timestamp > disputeDates.timeout) revert DisputeHasExpired();\n\n        // New dispute timeout should be after the current dispute timeout\n        if (_newDisputeTimeout <= disputeDates.timeout) revert InvalidDisputeTimeout();\n\n        // Update the timeout\n        disputeDates.timeout = _newDisputeTimeout;\n\n        // Notify watchers of state change\n        emit DisputeTimeoutExtended(_exchangeId, _newDisputeTimeout, sender);\n    }\n\n    /**\n     * @notice Expires the dispute and releases the funds.\n     *\n     * Emits a DisputeExpired event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Dispute is still valid\n     * - Dispute is in some state other than Resolving\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function expireDispute(uint256 _exchangeId) public override nonReentrant {\n        // Get the exchange, should be in disputed state\n        (Exchange storage exchange, ) = getValidExchange(_exchangeId, ExchangeState.Disputed);\n\n        // Fetch the dispute and dispute dates\n        (, Dispute storage dispute, DisputeDates storage disputeDates) = fetchDispute(_exchangeId);\n\n        // Make sure the dispute is in the resolving state\n        if (dispute.state != DisputeState.Resolving) revert InvalidState();\n\n        // make sure the dispute not expired already\n        if (block.timestamp <= disputeDates.timeout) revert DisputeStillValid();\n\n        // Finalize the dispute\n        finalizeDispute(_exchangeId, exchange, dispute, disputeDates, DisputeState.Retracted, 0);\n\n        // Notify watchers of state change\n        emit DisputeExpired(_exchangeId, msgSender());\n    }\n\n    /**\n     * @notice Expires a batch of disputes and releases the funds.\n     *\n     * Emits a DisputeExpired event for every dispute if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - For any dispute:\n     *   - Exchange does not exist\n     *   - Exchange is not in a Disputed state\n     *   - Dispute is still valid\n     *   - Dispute is in some state other than Resolving\n     *\n     * @param _exchangeIds - the array of ids of the associated exchanges\n     */\n    function expireDisputeBatch(uint256[] calldata _exchangeIds) external override {\n        for (uint256 i = 0; i < _exchangeIds.length; ) {\n            // create offer and update structs values to represent true state\n            expireDispute(_exchangeIds[i]);\n\n            unchecked {\n                i++;\n            }\n        }\n    }\n\n    /**\n     * @notice Resolves a dispute by providing the information about the funds split.\n     * Callable by the buyer or seller, but the caller must provide the resolution signed by the other party.\n     *\n     * Emits a DisputeResolved event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Specified buyer percent exceeds 100%\n     * - Dispute has expired (resolution period has ended and dispute was not escalated)\n     * - Exchange does not exist\n     * - Exchange is not in the Disputed state\n     * - Caller is neither the seller nor the buyer\n     * - Signature does not belong to the address of the other party\n     * - Dispute state is neither Resolving nor escalated\n     * - Dispute was escalated and escalation period has elapsed\n     *\n     * @param _exchangeId  - the id of the associated exchange\n     * @param _buyerPercent - percentage of the pot that goes to the buyer\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     */\n    function resolveDispute(\n        uint256 _exchangeId,\n        uint256 _buyerPercent,\n        bytes32 _sigR,\n        bytes32 _sigS,\n        uint8 _sigV\n    ) external override nonReentrant {\n        // buyer should get at most 100%\n        if (_buyerPercent > HUNDRED_PERCENT) revert InvalidBuyerPercent();\n\n        // Get the exchange, should be in disputed state\n        (Exchange storage exchange, ) = getValidExchange(_exchangeId, ExchangeState.Disputed);\n\n        // Fetch the dispute and dispute dates\n        (, Dispute storage dispute, DisputeDates storage disputeDates) = fetchDispute(_exchangeId);\n\n        // Make sure the dispute is in the resolving or escalated state\n        if (dispute.state != DisputeState.Resolving && dispute.state != DisputeState.Escalated) revert InvalidState();\n\n        // Make sure the dispute not expired already\n        if (block.timestamp > disputeDates.timeout) revert DisputeHasExpired();\n\n        // wrap the code in a separate block to avoid stack too deep error\n        {\n            // Fetch the offer to get the info who the seller is\n            (, Offer storage offer) = fetchOffer(exchange.offerId);\n\n            // get seller id to check if caller is the seller\n            (bool exists, uint256 sellerId) = getSellerIdByAssistant(msgSender());\n\n            // variable to store who the expected signer is\n            address expectedSigner;\n\n            // find out if the caller is the seller or the buyer, and which address should be the signer\n            if (exists && offer.sellerId == sellerId) {\n                // caller is the seller\n                // get the buyer's address, which should be the signer of the resolution\n                (, Buyer storage buyer) = fetchBuyer(exchange.buyerId);\n                expectedSigner = buyer.wallet;\n            } else {\n                uint256 buyerId;\n                (exists, buyerId) = getBuyerIdByWallet(msgSender());\n                if (!exists || buyerId != exchange.buyerId) revert NotBuyerOrSeller();\n\n                // caller is the buyer\n                // get the seller's address, which should be the signer of the resolution\n                (, Seller storage seller, ) = fetchSeller(offer.sellerId);\n                expectedSigner = seller.assistant;\n            }\n\n            // verify that the signature belongs to the expectedSigner\n            if (!EIP712Lib.verify(expectedSigner, hashResolution(_exchangeId, _buyerPercent), _sigR, _sigS, _sigV))\n                revert SignerAndSignatureDoNotMatch();\n        }\n\n        // finalize the dispute\n        finalizeDispute(_exchangeId, exchange, dispute, disputeDates, DisputeState.Resolved, _buyerPercent);\n\n        // Notify watchers of state change\n        emit DisputeResolved(_exchangeId, _buyerPercent, msgSender());\n    }\n\n    /**\n     * @notice Puts the dispute into the Escalated state.\n     *\n     * Caller must send (or for ERC20, approve the transfer of) the\n     * buyer escalation deposit percentage of the offer price, which\n     * will be added to the pot for resolution.\n     *\n     * Emits a DisputeEscalated event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Caller is not the buyer\n     * - Dispute is already expired\n     * - Dispute is not in a Resolving state\n     * - Dispute resolver is not specified (absolute zero offer)\n     * - Offer price is in native token and caller does not send enough\n     * - Offer price is in some ERC20 token and caller also sends native currency\n     * - If contract at token address does not support ERC20 function transferFrom\n     * - If calling transferFrom on token fails for some reason (e.g. protocol is not approved to transfer)\n     * - Received ERC20 token amount differs from the expected value\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function escalateDispute(uint256 _exchangeId) external payable override nonReentrant {\n        escalateDisputeInternal(_exchangeId);\n    }\n\n    /**\n     * @notice Decides a dispute by providing the information about the funds split. Callable by the dispute resolver specified in the offer.\n     *\n     * Emits a DisputeDecided event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Specified buyer percent exceeds 100%\n     * - Exchange does not exist\n     * - Exchange is not in the Disputed state\n     * - Caller is not the dispute resolver for this dispute\n     * - Dispute state is not Escalated\n     * - Dispute escalation response period has elapsed\n     *\n     * @param _exchangeId  - the id of the associated exchange\n     * @param _buyerPercent - percentage of the pot that goes to the buyer\n     */\n    function decideDispute(uint256 _exchangeId, uint256 _buyerPercent) external override nonReentrant {\n        // Buyer should get at most 100%\n        if (_buyerPercent > HUNDRED_PERCENT) revert InvalidBuyerPercent();\n\n        // Make sure the dispute is valid and the caller is the dispute resolver\n        (Exchange storage exchange, Dispute storage dispute, DisputeDates storage disputeDates) = disputeResolverChecks(\n            _exchangeId\n        );\n\n        // Finalize the dispute\n        finalizeDispute(_exchangeId, exchange, dispute, disputeDates, DisputeState.Decided, _buyerPercent);\n\n        // Notify watchers of state change\n        emit DisputeDecided(_exchangeId, _buyerPercent, msgSender());\n    }\n\n    /**\n     * @notice Enables dispute resolver to explicitly refuse to resolve a dispute in Escalated state and releases the funds.\n     *\n     * Emits an EscalatedDisputeRefused event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Dispute is in some state other than Escalated\n     * - Dispute escalation response period has elapsed\n     * - Caller is not the dispute resolver for this dispute\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function refuseEscalatedDispute(uint256 _exchangeId) external override nonReentrant {\n        // Make sure the dispute is valid and the caller is the dispute resolver\n        (Exchange storage exchange, Dispute storage dispute, DisputeDates storage disputeDates) = disputeResolverChecks(\n            _exchangeId\n        );\n\n        // Finalize the dispute\n        finalizeDispute(_exchangeId, exchange, dispute, disputeDates, DisputeState.Refused, 0);\n\n        // Notify watchers of state change\n        emit EscalatedDisputeRefused(_exchangeId, msgSender());\n    }\n\n    /**\n     * @notice Expires the dispute in escalated state and release the funds.\n     *\n     * Emits an EscalatedDisputeExpired event if successful.\n     *\n     * Reverts if:\n     * - The disputes region of protocol is paused\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Dispute is in some state other than Escalated\n     * - Dispute escalation period has not passed yet\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function expireEscalatedDispute(uint256 _exchangeId) external override nonReentrant {\n        // Get the exchange, should be in disputed state\n        (Exchange storage exchange, ) = getValidExchange(_exchangeId, ExchangeState.Disputed);\n\n        // Fetch the dispute and dispute dates\n        (, Dispute storage dispute, DisputeDates storage disputeDates) = fetchDispute(_exchangeId);\n\n        // Make sure the dispute is in the escalated state\n        if (dispute.state != DisputeState.Escalated) revert InvalidState();\n\n        // make sure the dispute escalation has expired already\n        if (block.timestamp <= disputeDates.timeout) revert DisputeStillValid();\n\n        // Finalize the dispute\n        finalizeDispute(_exchangeId, exchange, dispute, disputeDates, DisputeState.Refused, 0);\n\n        // Notify watchers of state change\n        emit EscalatedDisputeExpired(_exchangeId, msgSender());\n    }\n\n    /**\n     * @notice Transitions a dispute to a \"finalized\" state.\n     *\n     * Target state must be Retracted, Resolved, or Decided.\n     * Sets finalized date for exchange and dispute. Stores the resolution, if exists, and releases the funds.\n     *\n     * Reverts if the current dispute state is not Resolving or Escalated.\n     *\n     * @param _exchangeId  - the id of the associated exchange\n     * @param _exchange - pointer to exchange storage slot\n     * @param _dispute - pointer to dispute storage slot\n     * @param _disputeDates - pointer to disputeDates storage slot\n     * @param _targetState - target final state\n     * @param _buyerPercent - percentage of the pot that goes to the buyer\n     */\n    function finalizeDispute(\n        uint256 _exchangeId,\n        Exchange storage _exchange,\n        Dispute storage _dispute,\n        DisputeDates storage _disputeDates,\n        DisputeState _targetState,\n        uint256 _buyerPercent\n    ) internal disputesNotPaused {\n        if (_targetState == DisputeState.Escalated || _targetState == DisputeState.Resolving)\n            revert InvalidTargeDisputeState();\n\n        // update dispute and exchange\n        _disputeDates.finalized = block.timestamp;\n        _dispute.state = _targetState;\n        _exchange.finalizedDate = block.timestamp;\n\n        // store the resolution if it exists\n        if (_targetState == DisputeState.Resolved || _targetState == DisputeState.Decided) {\n            _dispute.buyerPercent = _buyerPercent;\n        }\n\n        // Release the funds\n        FundsLib.releaseFunds(_exchangeId);\n    }\n\n    /**\n     * @notice Gets the details about a given dispute.\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @return exists - true if the dispute exists\n     * @return dispute - the dispute details. See {BosonTypes.Dispute}\n     * @return disputeDates - the dispute dates details {BosonTypes.DisputeDates}\n     */\n    function getDispute(\n        uint256 _exchangeId\n    ) external view override returns (bool exists, Dispute memory dispute, DisputeDates memory disputeDates) {\n        return fetchDispute(_exchangeId);\n    }\n\n    /**\n     * @notice Gets the state of a given dispute.\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @return exists - true if the dispute exists\n     * @return state - the dispute state. See {BosonTypes.DisputeState}\n     */\n    function getDisputeState(uint256 _exchangeId) external view override returns (bool exists, DisputeState state) {\n        Dispute storage dispute;\n        (exists, dispute, ) = fetchDispute(_exchangeId);\n        if (exists) state = dispute.state;\n    }\n\n    /**\n     * @notice Gets the timeout of a given dispute.\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @return exists - true if the dispute exists\n     * @return timeout - the end of resolution period\n     */\n    function getDisputeTimeout(uint256 _exchangeId) external view override returns (bool exists, uint256 timeout) {\n        DisputeDates storage disputeDates;\n        (exists, , disputeDates) = fetchDispute(_exchangeId);\n        if (exists) timeout = disputeDates.timeout;\n    }\n\n    /**\n     * @notice Checks if the given dispute is in a Finalized state.\n     *\n     * Returns true if\n     * - Dispute state is Retracted, Resolved, Decided or Refused\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @return exists - true if the dispute exists\n     * @return isFinalized - true if the dispute is finalized\n     */\n    function isDisputeFinalized(uint256 _exchangeId) external view override returns (bool exists, bool isFinalized) {\n        Dispute storage dispute;\n\n        // Get the dispute\n        (exists, dispute, ) = fetchDispute(_exchangeId);\n\n        // if exists, set isFinalized to true if state is a valid finalized state\n        if (exists) {\n            // Check for finalized dispute state\n            isFinalized = (dispute.state == DisputeState.Retracted ||\n                dispute.state == DisputeState.Resolved ||\n                dispute.state == DisputeState.Decided ||\n                dispute.state == DisputeState.Refused);\n        }\n    }\n\n    /**\n     * @notice Validates that exchange and dispute are in the correct state and that the caller is the dispute resolver for this dispute.\n     *\n     * Reverts if:\n     * - Exchange does not exist\n     * - Exchange is not in a Disputed state\n     * - Dispute is in some state other than Escalated\n     * - Dispute escalation response period has elapsed\n     * - Caller is not the dispute resolver for this dispute\n     *\n     * @param _exchangeId - the id of the associated exchange\n     */\n    function disputeResolverChecks(\n        uint256 _exchangeId\n    ) internal view returns (Exchange storage exchange, Dispute storage dispute, DisputeDates storage disputeDates) {\n        // Get the exchange, should be in disputed state\n        (exchange, ) = getValidExchange(_exchangeId, ExchangeState.Disputed);\n\n        // Fetch the dispute and dispute dates\n        (, dispute, disputeDates) = fetchDispute(_exchangeId);\n\n        // Make sure the dispute is in the escalated state\n        if (dispute.state != DisputeState.Escalated) revert InvalidState();\n\n        // Make sure the dispute escalation period not expired already\n        if (block.timestamp > disputeDates.timeout) revert DisputeHasExpired();\n\n        // Fetch the offer to get the info who the seller is\n        (, Offer storage offer) = fetchOffer(exchange.offerId);\n\n        // get dispute resolver id to check if caller is the dispute resolver\n        uint256 disputeResolverId = protocolLookups().disputeResolverIdByAssistant[msgSender()];\n        if (disputeResolverId != fetchDisputeResolutionTerms(offer.id).disputeResolverId)\n            revert NotDisputeResolverAssistant();\n    }\n\n    /**\n     * @notice Returns hashed resolution information. Needed for the verfication in resolveDispute.\n     *\n     * @param _exchangeId - the id of the associated exchange\n     * @param _buyerPercent - percentage of the pot that goes to the buyer\n     */\n    function hashResolution(uint256 _exchangeId, uint256 _buyerPercent) internal pure returns (bytes32) {\n        return keccak256(abi.encode(RESOLUTION_TYPEHASH, _exchangeId, _buyerPercent));\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/FundsLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport \"../../domain/BosonConstants.sol\";\nimport { BosonErrors } from \"../../domain/BosonErrors.sol\";\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\nimport { EIP712Lib } from \"../libs/EIP712Lib.sol\";\nimport { ProtocolLib } from \"../libs/ProtocolLib.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IBosonFundsLibEvents } from \"../../interfaces/events/IBosonFundsEvents.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\n\n/**\n * @title FundsLib\n *\n * @dev\n */\nlibrary FundsLib {\n    using SafeERC20 for IERC20;\n\n    /**\n     * @notice Takes in the offer id and buyer id and encumbers buyer's and seller's funds during the commitToOffer.\n     * If offer is preminted, caller's funds are not encumbered, but the price is covered from the seller's funds.\n     *\n     * Emits FundsEncumbered event if successful.\n     *\n     * Reverts if:\n     * - Offer price is in native token and caller does not send enough\n     * - Offer price is in some ERC20 token and caller also sends native currency\n     * - Contract at token address does not support ERC20 function transferFrom\n     * - Calling transferFrom on token fails for some reason (e.g. protocol is not approved to transfer)\n     * - Seller has less funds available than sellerDeposit for non preminted offers\n     * - Seller has less funds available than sellerDeposit and price for preminted offers\n     * - Received ERC20 token amount differs from the expected value\n     *\n     * @param _offerId - id of the offer with the details\n     * @param _buyerId - id of the buyer\n     * @param _price - the price, either price discovered externally or set on offer creation\n     * @param _isPreminted - flag indicating if the offer is preminted\n     * @param _priceType - price type, either static or discovery\n     */\n    function encumberFunds(\n        uint256 _offerId,\n        uint256 _buyerId,\n        uint256 _price,\n        bool _isPreminted,\n        BosonTypes.PriceType _priceType\n    ) internal {\n        // Load protocol entities storage\n        ProtocolLib.ProtocolEntities storage pe = ProtocolLib.protocolEntities();\n\n        // get message sender\n        address sender = EIP712Lib.msgSender();\n\n        // fetch offer to get the exchange token, price and seller\n        // this will be called only from commitToOffer so we expect that exchange actually exist\n        BosonTypes.Offer storage offer = pe.offers[_offerId];\n        address exchangeToken = offer.exchangeToken;\n\n        // if offer is non-preminted, validate incoming payment\n        if (!_isPreminted) {\n            validateIncomingPayment(exchangeToken, _price);\n            emit IBosonFundsLibEvents.FundsEncumbered(_buyerId, exchangeToken, _price, sender);\n        }\n\n        bool isPriceDiscovery = _priceType == BosonTypes.PriceType.Discovery;\n\n        // decrease available funds\n        uint256 sellerId = offer.sellerId;\n        uint256 sellerFundsEncumbered = offer.sellerDeposit + (_isPreminted && !isPriceDiscovery ? _price : 0); // for preminted offer and price type is static, encumber also price from seller's available funds\n        decreaseAvailableFunds(sellerId, exchangeToken, sellerFundsEncumbered);\n\n        // notify external observers\n        emit IBosonFundsLibEvents.FundsEncumbered(sellerId, exchangeToken, sellerFundsEncumbered, sender);\n    }\n\n    /**\n     * @notice Validates that incoming payments matches expectation. If token is a native currency, it makes sure\n     * msg.value is correct. If token is ERC20, it transfers the value from the sender to the protocol.\n     *\n     * Emits ERC20 Transfer event in call stack if successful.\n     *\n     * Reverts if:\n     * - Offer price is in native token and caller does not send enough\n     * - Offer price is in some ERC20 token and caller also sends native currency\n     * - Contract at token address does not support ERC20 function transferFrom\n     * - Calling transferFrom on token fails for some reason (e.g. protocol is not approved to transfer)\n     * - Received ERC20 token amount differs from the expected value\n     *\n     * @param _exchangeToken - address of the token (0x for native currency)\n     * @param _value - value expected to receive\n     */\n    function validateIncomingPayment(address _exchangeToken, uint256 _value) internal {\n        if (_exchangeToken == address(0)) {\n            // if transfer is in the native currency, msg.value must match offer price\n            if (msg.value != _value) revert BosonErrors.InsufficientValueReceived();\n        } else {\n            // when price is in an erc20 token, transferring the native currency is not allowed\n            if (msg.value != 0) revert BosonErrors.NativeNotAllowed();\n\n            // if transfer is in ERC20 token, try to transfer the amount from buyer to the protocol\n            transferFundsToProtocol(_exchangeToken, _value);\n        }\n    }\n\n    /**\n     * @notice Takes in the exchange id and releases the funds to buyer and seller, depending on the state of the exchange.\n     * It is called only from finalizeExchange and finalizeDispute.\n     *\n     * Emits FundsReleased and/or ProtocolFeeCollected event if payoffs are warranted and transaction is successful.\n     *\n     * @param _exchangeId - exchange id\n     */\n    function releaseFunds(uint256 _exchangeId) internal {\n        // Load protocol entities storage\n        ProtocolLib.ProtocolEntities storage pe = ProtocolLib.protocolEntities();\n\n        // Get the exchange and its state\n        // Since this should be called only from certain functions from exchangeHandler and disputeHandler\n        // exchange must exist and be in a completed state, so that's not checked explicitly\n        BosonTypes.Exchange storage exchange = pe.exchanges[_exchangeId];\n\n        // Get offer from storage to get the details about sellerDeposit, price, sellerId, exchangeToken and buyerCancelPenalty\n        BosonTypes.Offer storage offer = pe.offers[exchange.offerId];\n\n        // calculate the payoffs depending on state exchange is in\n        BosonTypes.Payoff memory payoff;\n\n        BosonTypes.OfferFees storage offerFee = pe.offerFees[exchange.offerId];\n        uint256 offerPrice = offer.price;\n        BosonTypes.ExchangeCosts[] storage exchangeCosts = pe.exchangeCosts[_exchangeId];\n        uint256 lastPrice = exchangeCosts.length == 0 ? offerPrice : exchangeCosts[exchangeCosts.length - 1].price;\n        {\n            // scope to avoid stack too deep errors\n            BosonTypes.ExchangeState exchangeState = exchange.state;\n            uint256 sellerDeposit = offer.sellerDeposit;\n\n            if (exchangeState == BosonTypes.ExchangeState.Completed) {\n                // COMPLETED\n                payoff.protocol = offerFee.protocolFee;\n                // buyerPayoff is 0\n                payoff.agent = offerFee.agentFee;\n                payoff.seller = offerPrice + sellerDeposit - payoff.protocol - payoff.agent;\n            } else if (exchangeState == BosonTypes.ExchangeState.Revoked) {\n                // REVOKED\n                // sellerPayoff is 0\n                payoff.buyer = lastPrice + sellerDeposit;\n            } else if (exchangeState == BosonTypes.ExchangeState.Canceled) {\n                // CANCELED\n                uint256 buyerCancelPenalty = offer.buyerCancelPenalty;\n                payoff.seller = sellerDeposit + buyerCancelPenalty;\n                payoff.buyer = lastPrice - buyerCancelPenalty;\n            } else if (exchangeState == BosonTypes.ExchangeState.Disputed) {\n                // DISPUTED\n                // determine if buyerEscalationDeposit was encumbered or not\n                // if dispute was escalated, disputeDates.escalated is populated\n                uint256 buyerEscalationDeposit = pe.disputeDates[_exchangeId].escalated > 0\n                    ? pe.disputeResolutionTerms[exchange.offerId].buyerEscalationDeposit\n                    : 0;\n\n                // get the information about the dispute, which must exist\n                BosonTypes.Dispute storage dispute = pe.disputes[_exchangeId];\n                BosonTypes.DisputeState disputeState = dispute.state;\n\n                if (disputeState == BosonTypes.DisputeState.Retracted) {\n                    // RETRACTED - same as \"COMPLETED\"\n                    payoff.protocol = offerFee.protocolFee;\n                    payoff.agent = offerFee.agentFee;\n                    // buyerPayoff is 0\n                    payoff.seller =\n                        offerPrice +\n                        sellerDeposit -\n                        payoff.protocol -\n                        payoff.agent +\n                        buyerEscalationDeposit;\n                } else if (disputeState == BosonTypes.DisputeState.Refused) {\n                    // REFUSED\n                    payoff.seller = sellerDeposit;\n                    payoff.buyer = lastPrice + buyerEscalationDeposit;\n                } else {\n                    // RESOLVED or DECIDED\n                    uint256 commonPot = sellerDeposit + buyerEscalationDeposit;\n                    payoff.buyer = applyPercent(commonPot, dispute.buyerPercent);\n                    payoff.seller = commonPot - payoff.buyer;\n\n                    payoff.buyer = payoff.buyer + applyPercent(lastPrice, dispute.buyerPercent);\n                    payoff.seller = payoff.seller + offerPrice - applyPercent(offerPrice, dispute.buyerPercent);\n                }\n            }\n        }\n\n        address exchangeToken = offer.exchangeToken;\n\n        // Original seller and last buyer are done\n        // Release funds to intermediate sellers (if they exist)\n        // and add the protocol fee to the total\n        {\n            (uint256 sequentialProtocolFee, uint256 sequentialRoyalties) = releaseFundsToIntermediateSellers(\n                _exchangeId,\n                exchange.state,\n                offerPrice,\n                exchangeToken,\n                offer\n            );\n            payoff.seller += sequentialRoyalties;\n            payoff.protocol += sequentialProtocolFee;\n        }\n\n        // Store payoffs to availablefunds and notify the external observers\n        address sender = EIP712Lib.msgSender();\n        if (payoff.seller > 0) {\n            increaseAvailableFundsAndEmitEvent(_exchangeId, offer.sellerId, exchangeToken, payoff.seller, sender);\n        }\n        if (payoff.buyer > 0) {\n            increaseAvailableFundsAndEmitEvent(_exchangeId, exchange.buyerId, exchangeToken, payoff.buyer, sender);\n        }\n\n        if (payoff.protocol > 0) {\n            increaseAvailableFunds(0, exchangeToken, payoff.protocol);\n            emit IBosonFundsLibEvents.ProtocolFeeCollected(_exchangeId, exchangeToken, payoff.protocol, sender);\n        }\n        if (payoff.agent > 0) {\n            // Get the agent for offer\n            uint256 agentId = ProtocolLib.protocolLookups().agentIdByOffer[exchange.offerId];\n            increaseAvailableFundsAndEmitEvent(_exchangeId, agentId, exchangeToken, payoff.agent, sender);\n        }\n    }\n\n    /**\n     * @notice Takes the exchange id and releases the funds to original seller if offer.priceType is Discovery\n     * and to all intermediate resellers in case of sequential commit, depending on the state of the exchange.\n     * It is called only from releaseFunds. Protocol fee and royalties are calculated and returned to releaseFunds, where they are added to the total.\n     *\n     * Emits FundsReleased events for non zero payoffs.\n     *\n     * @param _exchangeId - exchange id\n     * @param _exchangeState - state of the exchange\n     * @param _initialPrice - initial price of the offer\n     * @param _exchangeToken - address of the token used for the exchange\n     * @param _offer - offer struct\n     * @return protocolFee - protocol fee from secondary sales\n     * @return sellerRoyalties - royalties from secondary sales collected for the seller\n     */\n    function releaseFundsToIntermediateSellers(\n        uint256 _exchangeId,\n        BosonTypes.ExchangeState _exchangeState,\n        uint256 _initialPrice,\n        address _exchangeToken,\n        BosonTypes.Offer storage _offer\n    ) internal returns (uint256 protocolFee, uint256 sellerRoyalties) {\n        BosonTypes.ExchangeCosts[] storage exchangeCosts;\n\n        // calculate effective price multiplier\n        uint256 effectivePriceMultiplier;\n        {\n            ProtocolLib.ProtocolEntities storage pe = ProtocolLib.protocolEntities();\n\n            exchangeCosts = pe.exchangeCosts[_exchangeId];\n\n            // if price type was static and no sequential commit happened, just return\n            if (exchangeCosts.length == 0) {\n                return (0, 0);\n            }\n\n            {\n                if (_exchangeState == BosonTypes.ExchangeState.Completed) {\n                    // COMPLETED, buyer pays full price\n                    effectivePriceMultiplier = HUNDRED_PERCENT;\n                } else if (\n                    _exchangeState == BosonTypes.ExchangeState.Revoked ||\n                    _exchangeState == BosonTypes.ExchangeState.Canceled\n                ) {\n                    // REVOKED or CANCELED, buyer pays nothing (buyerCancelPenalty is not considered payment)\n                    effectivePriceMultiplier = 0;\n                } else if (_exchangeState == BosonTypes.ExchangeState.Disputed) {\n                    // DISPUTED\n                    // get the information about the dispute, which must exist\n                    BosonTypes.Dispute storage dispute = pe.disputes[_exchangeId];\n                    BosonTypes.DisputeState disputeState = dispute.state;\n\n                    if (disputeState == BosonTypes.DisputeState.Retracted) {\n                        // RETRACTED - same as \"COMPLETED\"\n                        effectivePriceMultiplier = HUNDRED_PERCENT;\n                    } else if (disputeState == BosonTypes.DisputeState.Refused) {\n                        // REFUSED, buyer pays nothing\n                        effectivePriceMultiplier = 0;\n                    } else {\n                        // RESOLVED or DECIDED\n                        effectivePriceMultiplier = HUNDRED_PERCENT - dispute.buyerPercent;\n                    }\n                }\n            }\n        }\n\n        uint256 resellerBuyPrice = _initialPrice; // the price that reseller paid for the voucher\n        address msgSender = EIP712Lib.msgSender();\n        uint256 len = exchangeCosts.length;\n        for (uint256 i = 0; i < len; ) {\n            // Since all elements of exchangeCosts[i] are used it makes sense to copy them to memory\n            BosonTypes.ExchangeCosts memory secondaryCommit = exchangeCosts[i];\n\n            // amount to be released\n            uint256 currentResellerAmount;\n\n            // inside the scope to avoid stack too deep error\n            {\n                if (effectivePriceMultiplier > 0) {\n                    protocolFee =\n                        protocolFee +\n                        applyPercent(secondaryCommit.protocolFeeAmount, effectivePriceMultiplier);\n                    sellerRoyalties += distributeRoyalties(\n                        _exchangeId,\n                        _offer,\n                        secondaryCommit,\n                        effectivePriceMultiplier\n                    );\n                }\n\n                // secondary price without protocol fee and royalties\n                uint256 reducedSecondaryPrice = secondaryCommit.price -\n                    secondaryCommit.protocolFeeAmount -\n                    secondaryCommit.royaltyAmount;\n\n                // Calculate amount to be released to the reseller:\n                // + part of the price that they paid (relevant for unhappy paths)\n                // + price of the voucher that they sold reduced for part that goes to next reseller, royalties and protocol fee\n                // - immediate payout that was released already during the sequential commit\n                currentResellerAmount =\n                    applyPercent(resellerBuyPrice, (HUNDRED_PERCENT - effectivePriceMultiplier)) +\n                    secondaryCommit.price -\n                    applyPercent(secondaryCommit.price, (HUNDRED_PERCENT - effectivePriceMultiplier)) -\n                    applyPercent(secondaryCommit.protocolFeeAmount, effectivePriceMultiplier) -\n                    applyPercent(secondaryCommit.royaltyAmount, effectivePriceMultiplier) -\n                    Math.min(resellerBuyPrice, reducedSecondaryPrice);\n\n                resellerBuyPrice = secondaryCommit.price;\n            }\n\n            if (currentResellerAmount > 0) {\n                increaseAvailableFundsAndEmitEvent(\n                    _exchangeId,\n                    secondaryCommit.resellerId,\n                    _exchangeToken,\n                    currentResellerAmount,\n                    msgSender\n                );\n            }\n\n            unchecked {\n                i++;\n            }\n        }\n    }\n\n    /**\n     * @notice Forwards values to increaseAvailableFunds and emits notifies external listeners.\n     *\n     * Emits FundsReleased events\n     *\n     * @param _exchangeId - exchange id\n     * @param _entityId - id of the entity to which the funds are released\n     * @param _tokenAddress - address of the token used for the exchange\n     * @param _amount - amount of tokens to be released\n     * @param _sender - address of the sender that executed the transaction\n     */\n    function increaseAvailableFundsAndEmitEvent(\n        uint256 _exchangeId,\n        uint256 _entityId,\n        address _tokenAddress,\n        uint256 _amount,\n        address _sender\n    ) internal {\n        increaseAvailableFunds(_entityId, _tokenAddress, _amount);\n        emit IBosonFundsLibEvents.FundsReleased(_exchangeId, _entityId, _tokenAddress, _amount, _sender);\n    }\n\n    /**\n     * @notice Tries to transfer tokens from the caller to the protocol.\n     *\n     * Emits ERC20 Transfer event in call stack if successful.\n     *\n     * Reverts if:\n     * - Contract at token address does not support ERC20 function transferFrom\n     * - Calling transferFrom on token fails for some reason (e.g. protocol is not approved to transfer)\n     * - Received ERC20 token amount differs from the expected value\n     *\n     * @param _tokenAddress - address of the token to be transferred\n     * @param _from - address to transfer funds from\n     * @param _amount - amount to be transferred\n     */\n    function transferFundsToProtocol(address _tokenAddress, address _from, uint256 _amount) internal {\n        if (_amount > 0) {\n            // protocol balance before the transfer\n            uint256 protocolTokenBalanceBefore = IERC20(_tokenAddress).balanceOf(address(this));\n\n            // transfer ERC20 tokens from the caller\n            IERC20(_tokenAddress).safeTransferFrom(_from, address(this), _amount);\n\n            // protocol balance after the transfer\n            uint256 protocolTokenBalanceAfter = IERC20(_tokenAddress).balanceOf(address(this));\n\n            // make sure that expected amount of tokens was transferred\n            if (protocolTokenBalanceAfter - protocolTokenBalanceBefore != _amount)\n                revert BosonErrors.InsufficientValueReceived();\n        }\n    }\n\n    /**\n     * @notice Same as transferFundsToProtocol(address _tokenAddress, address _from, uint256 _amount),\n     * but _from is message sender\n     *\n     * @param _tokenAddress - address of the token to be transferred\n     * @param _amount - amount to be transferred\n     */\n    function transferFundsToProtocol(address _tokenAddress, uint256 _amount) internal {\n        transferFundsToProtocol(_tokenAddress, EIP712Lib.msgSender(), _amount);\n    }\n\n    /**\n     * @notice Tries to transfer native currency or tokens from the protocol to the recipient.\n     *\n     * Emits FundsWithdrawn event if successful.\n     * Emits ERC20 Transfer event in call stack if ERC20 token is withdrawn and transfer is successful.\n     *\n     * Reverts if:\n     * - Transfer of native currency is not successful (i.e. recipient is a contract which reverted)\n     * - Contract at token address does not support ERC20 function transfer\n     * - Available funds is less than amount to be decreased\n     *\n     * @param _entityId - id of entity for which funds should be decreased, or 0 for protocol\n     * @param _tokenAddress - address of the token to be transferred\n     * @param _to - address of the recipient\n     * @param _amount - amount to be transferred\n     */\n    function transferFundsFromProtocol(\n        uint256 _entityId,\n        address _tokenAddress,\n        address payable _to,\n        uint256 _amount\n    ) internal {\n        // first decrease the amount to prevent the reentrancy attack\n        decreaseAvailableFunds(_entityId, _tokenAddress, _amount);\n\n        // try to transfer the funds\n        transferFundsFromProtocol(_tokenAddress, _to, _amount);\n\n        // notify the external observers\n        emit IBosonFundsLibEvents.FundsWithdrawn(_entityId, _to, _tokenAddress, _amount, EIP712Lib.msgSender());\n    }\n\n    /**\n     * @notice Tries to transfer native currency or tokens from the protocol to the recipient.\n     *\n     * Emits ERC20 Transfer event in call stack if ERC20 token is withdrawn and transfer is successful.\n     *\n     * Reverts if:\n     * - Transfer of native currency is not successful (i.e. recipient is a contract which reverted)\n     * - Contract at token address does not support ERC20 function transfer\n     * - Available funds is less than amount to be decreased\n     *\n     * @param _tokenAddress - address of the token to be transferred\n     * @param _to - address of the recipient\n     * @param _amount - amount to be transferred\n     */\n    function transferFundsFromProtocol(address _tokenAddress, address payable _to, uint256 _amount) internal {\n        // try to transfer the funds\n        if (_tokenAddress == address(0)) {\n            // transfer native currency\n            (bool success, ) = _to.call{ value: _amount }(\"\");\n            if (!success) revert BosonErrors.TokenTransferFailed();\n        } else {\n            // transfer ERC20 tokens\n            IERC20(_tokenAddress).safeTransfer(_to, _amount);\n        }\n    }\n\n    /**\n     * @notice Increases the amount, available to withdraw or use as a seller deposit.\n     *\n     * @param _entityId - id of entity for which funds should be increased, or 0 for protocol\n     * @param _tokenAddress - funds contract address or zero address for native currency\n     * @param _amount - amount to be credited\n     */\n    function increaseAvailableFunds(uint256 _entityId, address _tokenAddress, uint256 _amount) internal {\n        ProtocolLib.ProtocolLookups storage pl = ProtocolLib.protocolLookups();\n\n        // if the current amount of token is 0, the token address must be added to the token list\n        mapping(address => uint256) storage availableFunds = pl.availableFunds[_entityId];\n        if (availableFunds[_tokenAddress] == 0) {\n            address[] storage tokenList = pl.tokenList[_entityId];\n            tokenList.push(_tokenAddress);\n            //Set index mapping. Should be index in tokenList array + 1\n            pl.tokenIndexByAccount[_entityId][_tokenAddress] = tokenList.length;\n        }\n\n        // update the available funds\n        availableFunds[_tokenAddress] += _amount;\n    }\n\n    /**\n     * @notice Decreases the amount available to withdraw or use as a seller deposit.\n     *\n     * Reverts if:\n     * - Available funds is less than amount to be decreased\n     *\n     * @param _entityId - id of entity for which funds should be decreased, or 0 for protocol\n     * @param _tokenAddress - funds contract address or zero address for native currency\n     * @param _amount - amount to be taken away\n     */\n    function decreaseAvailableFunds(uint256 _entityId, address _tokenAddress, uint256 _amount) internal {\n        if (_amount > 0) {\n            ProtocolLib.ProtocolLookups storage pl = ProtocolLib.protocolLookups();\n\n            // get available funds from storage\n            mapping(address => uint256) storage availableFunds = pl.availableFunds[_entityId];\n            uint256 entityFunds = availableFunds[_tokenAddress];\n\n            // make sure that seller has enough funds in the pool and reduce the available funds\n            if (entityFunds < _amount) revert BosonErrors.InsufficientAvailableFunds();\n\n            // Use unchecked to optimize execution cost. The math is safe because of the require above.\n            unchecked {\n                availableFunds[_tokenAddress] = entityFunds - _amount;\n            }\n\n            // if available funds are totally emptied, the token address is removed from the seller's tokenList\n            if (entityFunds == _amount) {\n                // Get the index in the tokenList array, which is 1 less than the tokenIndexByAccount index\n                address[] storage tokenList = pl.tokenList[_entityId];\n                uint256 lastTokenIndex = tokenList.length - 1;\n                mapping(address => uint256) storage entityTokens = pl.tokenIndexByAccount[_entityId];\n                uint256 index = entityTokens[_tokenAddress] - 1;\n\n                // if target is last index then only pop and delete are needed\n                // otherwise, we overwrite the target with the last token first\n                if (index != lastTokenIndex) {\n                    // Need to fill gap caused by delete if more than one element in storage array\n                    address tokenToMove = tokenList[lastTokenIndex];\n                    // Copy the last token in the array to this index to fill the gap\n                    tokenList[index] = tokenToMove;\n                    // Reset index mapping. Should be index in tokenList array + 1\n                    entityTokens[tokenToMove] = index + 1;\n                }\n                // Delete last token address in the array, which was just moved to fill the gap\n                tokenList.pop();\n                // Delete from index mapping\n                delete entityTokens[_tokenAddress];\n            }\n        }\n    }\n\n    /**\n     * @notice Distributes the royalties to external recipients and seller's treasury.\n     *\n     * @param _offer - storage pointer to the offer\n     * @param _secondaryCommit - information about the secondary commit (royaltyInfoIndex, price, escrowedRoyaltyAmount)\n     * @param _effectivePriceMultiplier - multiplier for the price, depending on the state of the exchange\n     */\n    function distributeRoyalties(\n        uint256 _exchangeId,\n        BosonTypes.Offer storage _offer,\n        BosonTypes.ExchangeCosts memory _secondaryCommit,\n        uint256 _effectivePriceMultiplier\n    ) internal returns (uint256 sellerRoyalties) {\n        address sender = EIP712Lib.msgSender();\n        address exchangeToken = _offer.exchangeToken;\n        BosonTypes.RoyaltyInfo storage _royaltyInfo = _offer.royaltyInfo[_secondaryCommit.royaltyInfoIndex];\n        uint256 len = _royaltyInfo.recipients.length;\n        uint256 totalAmount;\n        uint256 effectivePrice = applyPercent(_secondaryCommit.price, _effectivePriceMultiplier);\n        ProtocolLib.ProtocolLookups storage pl = ProtocolLib.protocolLookups();\n        for (uint256 i = 0; i < len; ) {\n            address payable recipient = _royaltyInfo.recipients[i];\n            uint256 amount = applyPercent(_royaltyInfo.bps[i], effectivePrice);\n            totalAmount += amount;\n            if (recipient == address(0)) {\n                // goes to seller's treasury\n                sellerRoyalties += amount;\n            } else {\n                // Make funds available to withdraw\n                if (amount > 0) {\n                    increaseAvailableFundsAndEmitEvent(\n                        _exchangeId,\n                        pl.royaltyRecipientIdByWallet[recipient],\n                        exchangeToken,\n                        amount,\n                        sender\n                    );\n                }\n            }\n\n            unchecked {\n                i++;\n            }\n        }\n\n        // if there is a remainder due to rounding, it goes to the seller's treasury\n        sellerRoyalties =\n            sellerRoyalties +\n            applyPercent(_secondaryCommit.royaltyAmount, _effectivePriceMultiplier) -\n            totalAmount;\n    }\n\n    /**\n     * @notice Calulates the percentage of the amount.\n     *\n     * @param _amount - amount to be used for the calculation\n     * @param _percent - percentage to be calculated, in basis points (1% = 100, 100% = 10000)\n     */\n    function applyPercent(uint256 _amount, uint256 _percent) internal pure returns (uint256) {\n        if (_percent == HUNDRED_PERCENT) return _amount;\n        if (_percent == 0) return 0;\n\n        return (_amount * _percent) / HUNDRED_PERCENT;\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": {}
    }
  }}