creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "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/IBosonAccountEvents.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\n\n/**\n * @title IBosonAccountEvents\n *\n * @notice Defines events related to management of accounts within the protocol.\n */\ninterface IBosonAccountEvents {\n    event SellerCreated(\n        uint256 indexed sellerId,\n        BosonTypes.Seller seller,\n        address voucherCloneAddress,\n        BosonTypes.AuthToken authToken,\n        address indexed executedBy\n    );\n    event SellerUpdatePending(\n        uint256 indexed sellerId,\n        BosonTypes.Seller pendingSeller,\n        BosonTypes.AuthToken pendingAuthToken,\n        address indexed executedBy\n    );\n    event SellerUpdateApplied(\n        uint256 indexed sellerId,\n        BosonTypes.Seller seller,\n        BosonTypes.Seller pendingSeller,\n        BosonTypes.AuthToken authToken,\n        BosonTypes.AuthToken pendingAuthToken,\n        address indexed executedBy\n    );\n    event RoyaltyRecipientsChanged(\n        uint256 indexed sellerId,\n        BosonTypes.RoyaltyRecipientInfo[] royaltyRecipients,\n        address indexed executedBy\n    );\n    event BuyerCreated(uint256 indexed buyerId, BosonTypes.Buyer buyer, address indexed executedBy);\n    event BuyerUpdated(uint256 indexed buyerId, BosonTypes.Buyer buyer, address indexed executedBy);\n    event AgentUpdated(uint256 indexed agentId, BosonTypes.Agent agent, address indexed executedBy);\n    event DisputeResolverCreated(\n        uint256 indexed disputeResolverId,\n        BosonTypes.DisputeResolver disputeResolver,\n        BosonTypes.DisputeResolverFee[] disputeResolverFees,\n        uint256[] sellerAllowList,\n        address indexed executedBy\n    );\n    event DisputeResolverUpdatePending(\n        uint256 indexed disputeResolverId,\n        BosonTypes.DisputeResolver pendingDisputeResolver,\n        address indexed executedBy\n    );\n    event DisputeResolverUpdateApplied(\n        uint256 indexed disputeResolverId,\n        BosonTypes.DisputeResolver disputeResolver,\n        BosonTypes.DisputeResolver pendingDisputeResolver,\n        address indexed executedBy\n    );\n    event DisputeResolverFeesAdded(\n        uint256 indexed disputeResolverId,\n        BosonTypes.DisputeResolverFee[] disputeResolverFees,\n        address indexed executedBy\n    );\n    event DisputeResolverFeesRemoved(\n        uint256 indexed disputeResolverId,\n        address[] feeTokensRemoved,\n        address indexed executedBy\n    );\n    event AllowedSellersAdded(uint256 indexed disputeResolverId, uint256[] addedSellers, address indexed executedBy);\n    event AllowedSellersRemoved(\n        uint256 indexed disputeResolverId,\n        uint256[] removedSellers,\n        address indexed executedBy\n    );\n    event AgentCreated(uint256 indexed agentId, BosonTypes.Agent agent, address indexed executedBy);\n    event CollectionCreated(\n        uint256 indexed sellerId,\n        uint256 collectionIndex,\n        address collectionAddress,\n        string indexed externalId,\n        address indexed executedBy\n    );\n}\n"
      },
      "contracts/interfaces/handlers/IBosonDisputeResolverHandler.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 IBosonDisputeResolverHandler\n *\n * @notice Handles creation, update, retrieval of dispute resolvers within the protocol.\n *\n * The ERC-165 identifier for this interface is: 0x50564987\n */\ninterface IBosonDisputeResolverHandler {\n    /**\n     * @notice Creates a dispute resolver.\n     *\n     * Emits a DisputeResolverCreated event if successful.\n     *\n     * Reverts if:\n     * - Caller is not the supplied admin and assistant\n     * - Supplied clerk is not a zero address\n     * - The dispute resolvers region of protocol is paused\n     * - Any address is zero address\n     * - Any address is not unique to this dispute resolver\n     * - EscalationResponsePeriod is invalid\n     * - Some seller does not exist\n     * - Some seller id is duplicated\n     * - DisputeResolver is not active (if active == false)\n     * - Fee amount is a non-zero value. Protocol doesn't yet support fees for dispute resolvers\n     *\n     * @param _disputeResolver - the fully populated struct with dispute resolver id set to 0x0\n     * @param _disputeResolverFees - list of fees dispute resolver charges per token type. Zero address is native currency. See {BosonTypes.DisputeResolverFee}\n     *                               feeAmount will be ignored because protocol doesn't yet support fees yet but DR still needs to provide array of fees to choose supported tokens\n     * @param _sellerAllowList - list of ids of sellers that can choose this dispute resolver. If empty, there are no restrictions on which seller can chose it.\n     */\n    function createDisputeResolver(\n        BosonTypes.DisputeResolver memory _disputeResolver,\n        BosonTypes.DisputeResolverFee[] calldata _disputeResolverFees,\n        uint256[] calldata _sellerAllowList\n    ) external;\n\n    /**\n     * @notice Updates treasury address, escalationResponsePeriod or metadataUri if changed. Puts admin and assistant in pending queue, if changed.\n     *         Pending updates can be completed by calling the optInToDisputeResolverUpdate function.\n     *\n     *         Update doesn't include DisputeResolverFees, allowed seller list or active flag.\n     *         All DisputeResolver fields should be filled, even those staying the same.\n     *         Use removeFeesFromDisputeResolver and addFeesToDisputeResolver to add and remove fees.\n     *         Use addSellersToAllowList and removeSellersFromAllowList to add and remove allowed sellers.\n     *\n     * @dev    Active flag passed in by caller will be ignored. The value from storage will be used.\n     *\n     * Emits a DisputeResolverUpdated event if successful.\n     * Emits a DisputeResolverUpdatePending event if the dispute resolver has requested an update for admin or assistant.\n     * Owner(s) of new addresses for admin, assistant must opt-in to the update.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address of the stored dispute resolver\n     * - Any address is not unique to this dispute resolver\n     * - Supplied clerk is not a zero address\n     * - Dispute resolver does not exist\n     * - EscalationResponsePeriod is invalid\n     * - No field has been updated or requested to be updated\n     *\n     * @param _disputeResolver - the fully populated dispute resolver struct\n     */\n    function updateDisputeResolver(BosonTypes.DisputeResolver memory _disputeResolver) external;\n\n    /**\n     * @notice Opt-in to a pending dispute resolver update\n     *\n     * Emits a DisputeResolverUpdateApplied event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolver region of protocol is paused\n     * - Addresses are not unique to this dispute resolver\n     * - Caller address is not pending update for the field being updated\n     * - No pending update exists for this dispute resolver\n     * - Dispute resolver tries to update the clerk\n     *\n     * @param _disputeResolverId - disputeResolver id\n     * @param _fieldsToUpdate - fields to update, see DisputeResolverUpdateFields enum\n     */\n    function optInToDisputeResolverUpdate(\n        uint256 _disputeResolverId,\n        BosonTypes.DisputeResolverUpdateFields[] calldata _fieldsToUpdate\n    ) external;\n\n    /**\n     * @notice Adds DisputeResolverFees to an existing dispute resolver.\n     *\n     * Emits a DisputeResolverFeesAdded event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address associated with the dispute resolver account\n     * - Dispute resolver does not exist\n     * - Number of DisputeResolverFee structs in array is zero\n     * - DisputeResolverFee array contains duplicates\n     * - Fee amount is a non-zero value. Protocol doesn't yet support fees for dispute resolvers\n     *\n     * @param _disputeResolverId - id of the dispute resolver\n     * @param _disputeResolverFees - list of fees dispute resolver charges per token type. Zero address is native currency. See {BosonTypes.DisputeResolverFee}\n     *                               feeAmount will be ignored because protocol doesn't yet support fees yet but DR still needs to provide array of fees to choose supported tokens\n     */\n    function addFeesToDisputeResolver(\n        uint256 _disputeResolverId,\n        BosonTypes.DisputeResolverFee[] calldata _disputeResolverFees\n    ) external;\n\n    /**\n     * @notice Removes DisputeResolverFees from  an existing dispute resolver.\n     *\n     * Emits a DisputeResolverFeesRemoved event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address associated with the dispute resolver account\n     * - Dispute resolver does not exist\n     * - Number of DisputeResolverFee structs in array is zero\n     * - DisputeResolverFee does not exist for the dispute resolver\n     *\n     * @param _disputeResolverId - id of the dispute resolver\n     * @param _feeTokenAddresses - list of addresses of dispute resolver fee tokens to remove\n     */\n    function removeFeesFromDisputeResolver(uint256 _disputeResolverId, address[] calldata _feeTokenAddresses) external;\n\n    /**\n     * @notice Adds seller ids to set of ids allowed to choose the given dispute resolver for an offer.\n     *\n     * Emits an AllowedSellersAdded event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address associated with the dispute resolver account\n     * - Dispute resolver does not exist\n     * - Number of seller ids in array is zero\n     * - Some seller does not exist\n     * - Seller id is already approved\n     *\n     * @param _disputeResolverId - id of the dispute resolver\n     * @param _sellerAllowList - List of seller ids to add to allowed list\n     */\n    function addSellersToAllowList(uint256 _disputeResolverId, uint256[] calldata _sellerAllowList) external;\n\n    /**\n     * @notice Removes seller ids from set of ids allowed to choose the given dispute resolver for an offer.\n     *\n     * Emits an AllowedSellersRemoved event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address associated with the dispute resolver account\n     * - Dispute resolver does not exist\n     * - Number of seller ids structs in array is zero\n     * - Seller id is not approved\n     *\n     * @param _disputeResolverId - id of the dispute resolver\n     * @param _sellerAllowList - list of seller ids to remove from allowed list\n     */\n    function removeSellersFromAllowList(uint256 _disputeResolverId, uint256[] calldata _sellerAllowList) external;\n\n    /**\n     * @notice Gets the details about a dispute resolver.\n     *\n     * @param _disputeResolverId - the id of the dispute resolver to check\n     * @return exists - the dispute resolver was found\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     * @return sellerAllowList - list of sellers that are allowed to choose this dispute resolver\n     */\n    function getDisputeResolver(\n        uint256 _disputeResolverId\n    )\n        external\n        view\n        returns (\n            bool exists,\n            BosonTypes.DisputeResolver memory disputeResolver,\n            BosonTypes.DisputeResolverFee[] memory disputeResolverFees,\n            uint256[] memory sellerAllowList\n        );\n\n    /**\n     * @notice Gets the details about a dispute resolver by an address associated with that dispute resolver: assistant, or admin address.\n     *\n     * @param _associatedAddress - the address associated with the dispute resolver. Must be an assistant, or admin address.\n     * @return exists - the dispute resolver was found\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     * @return sellerAllowList - list of sellers that are allowed to chose this dispute resolver\n     */\n    function getDisputeResolverByAddress(\n        address _associatedAddress\n    )\n        external\n        view\n        returns (\n            bool exists,\n            BosonTypes.DisputeResolver memory disputeResolver,\n            BosonTypes.DisputeResolverFee[] memory disputeResolverFees,\n            uint256[] memory sellerAllowList\n        );\n\n    /**\n     * @notice Checks whether given sellers are allowed to choose the given dispute resolver.\n     *\n     * @param _disputeResolverId - id of dispute resolver to check\n     * @param _sellerIds - list of seller ids to check\n     * @return sellerAllowed - array with indicator (true/false) if seller is allowed to choose the dispute resolver. Index in this array corresponds to indices of the incoming _sellerIds\n     */\n    function areSellersAllowed(\n        uint256 _disputeResolverId,\n        uint256[] calldata _sellerIds\n    ) external view returns (bool[] memory sellerAllowed);\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/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/DisputeResolverHandlerFacet.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity 0.8.22;\n\nimport \"../../domain/BosonConstants.sol\";\nimport { IBosonAccountEvents } from \"../../interfaces/events/IBosonAccountEvents.sol\";\nimport { IBosonDisputeResolverHandler } from \"../../interfaces/handlers/IBosonDisputeResolverHandler.sol\";\nimport { ProtocolBase } from \"../bases/ProtocolBase.sol\";\nimport { ProtocolLib } from \"../libs/ProtocolLib.sol\";\n\n/**\n * @title DisputeResolverHandlerFacet\n *\n * @notice Handles dispute resolver account management requests and queries\n */\ncontract DisputeResolverHandlerFacet is IBosonDisputeResolverHandler, IBosonAccountEvents, ProtocolBase {\n    /**\n     * @notice Initializes facet.\n     */\n    function initialize() public {\n        // No-op initializer.\n        // - needed by the deployment script, which expects a no-args initializer on facets other than the config handler\n        // - exception here because IBosonAccountHandler is contributed to by multiple facets which do not have their own individual interfaces\n    }\n\n    /**\n     * @notice Creates a dispute resolver.\n     *\n     * Emits a DisputeResolverCreated event if successful.\n     *\n     * Reverts if:\n     * - Caller is not the supplied admin and assistant\n     * - Supplied clerk is not a zero address\n     * - The dispute resolvers region of protocol is paused\n     * - Any address is zero address\n     * - Any address is not unique to this dispute resolver\n     * - EscalationResponsePeriod is invalid\n     * - Some seller does not exist\n     * - Some seller id is duplicated\n     * - DisputeResolver is not active (if active == false)\n     * - Fee amount is a non-zero value. Protocol doesn't yet support fees for dispute resolvers\n     *\n     * @param _disputeResolver - the fully populated struct with dispute resolver id set to 0x0\n     * @param _disputeResolverFees - list of fees dispute resolver charges per token type. Zero address is native currency. See {BosonTypes.DisputeResolverFee}\n     *                               feeAmount will be ignored because protocol doesn't yet support fees yet but DR still needs to provide array of fees to choose supported tokens\n     * @param _sellerAllowList - list of ids of sellers that can choose this dispute resolver. If empty, there are no restrictions on which seller can chose it.\n     */\n    function createDisputeResolver(\n        DisputeResolver memory _disputeResolver,\n        DisputeResolverFee[] calldata _disputeResolverFees,\n        uint256[] calldata _sellerAllowList\n    ) external disputeResolversNotPaused nonReentrant {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Check for zero address\n        if (\n            _disputeResolver.admin == address(0) ||\n            _disputeResolver.assistant == address(0) ||\n            _disputeResolver.treasury == address(0)\n        ) revert InvalidAddress();\n\n        // Check active is not set to false\n        if (!_disputeResolver.active) revert MustBeActive();\n\n        // Scope to avoid stack too deep errors\n        {\n            // Get message sender\n            address sender = msgSender();\n\n            // Check that caller is the supplied admin and assistant\n            if (_disputeResolver.admin != sender || _disputeResolver.assistant != sender) revert NotAdminAndAssistant();\n            if (_disputeResolver.clerk != address(0)) revert ClerkDeprecated();\n        }\n\n        // Get the next account id and increment the counter\n        uint256 disputeResolverId = protocolCounters().nextAccountId++;\n\n        // Check that the addresses are unique to one dispute resolver id, across all rolls\n        mapping(address => uint256) storage disputeResolverIdByAssistant = lookups.disputeResolverIdByAssistant;\n        mapping(address => uint256) storage disputeResolverIdByAdmin = lookups.disputeResolverIdByAdmin;\n        if (\n            disputeResolverIdByAssistant[_disputeResolver.assistant] != 0 ||\n            disputeResolverIdByAssistant[_disputeResolver.admin] != 0 ||\n            disputeResolverIdByAdmin[_disputeResolver.admin] != 0 ||\n            disputeResolverIdByAdmin[_disputeResolver.assistant] != 0\n        ) {\n            revert DisputeResolverAddressMustBeUnique();\n        }\n\n        // Scope to avoid stack too deep errors\n        {\n            // Cache protocol limits for reference\n            ProtocolLib.ProtocolLimits storage limits = protocolLimits();\n\n            // Escalation period must be greater than zero and less than or equal to the max allowed\n            if (\n                _disputeResolver.escalationResponsePeriod == 0 ||\n                _disputeResolver.escalationResponsePeriod > limits.maxEscalationResponsePeriod\n            ) revert InvalidEscalationPeriod();\n        }\n\n        _disputeResolver.id = disputeResolverId;\n\n        // Get storage location for dispute resolver fees\n        (, , DisputeResolverFee[] storage disputeResolverFees) = fetchDisputeResolver(_disputeResolver.id);\n\n        // Set dispute resolver fees. Must loop because calldata structs cannot be converted to storage structs\n        mapping(address => uint256) storage disputeResolverFeeTokens = lookups.disputeResolverFeeTokenIndex[\n            _disputeResolver.id\n        ];\n\n        for (uint256 i = 0; i < _disputeResolverFees.length; ) {\n            if (disputeResolverFeeTokens[_disputeResolverFees[i].tokenAddress] != 0)\n                revert DuplicateDisputeResolverFees();\n\n            // Protocol doesn't yet support DR fees\n            if (_disputeResolverFees[i].feeAmount != 0) revert FeeAmountNotYetSupported();\n\n            disputeResolverFees.push(_disputeResolverFees[i]);\n\n            // Set index mapping. Should be index in disputeResolverFees array + 1\n            disputeResolverFeeTokens[_disputeResolverFees[i].tokenAddress] = disputeResolverFees.length;\n\n            unchecked {\n                i++;\n            }\n        }\n\n        storeDisputeResolver(_disputeResolver);\n        storeSellerAllowList(disputeResolverId, _sellerAllowList);\n\n        // Notify watchers of state change\n        emit DisputeResolverCreated(\n            _disputeResolver.id,\n            _disputeResolver,\n            _disputeResolverFees,\n            _sellerAllowList,\n            msgSender()\n        );\n    }\n\n    /**\n     * @notice Updates treasury address, escalationResponsePeriod or metadataUri if changed. Puts admin and assistant in pending queue, if changed.\n     *         Pending updates can be completed by calling the optInToDisputeResolverUpdate function.\n     *\n     *         Update doesn't include DisputeResolverFees, allowed seller list or active flag.\n     *         All DisputeResolver fields should be filled, even those staying the same.\n     *         Use removeFeesFromDisputeResolver and addFeesToDisputeResolver to add and remove fees.\n     *         Use addSellersToAllowList and removeSellersFromAllowList to add and remove allowed sellers.\n     *\n     * @dev    Active flag passed in by caller will be ignored. The value from storage will be used.\n     *\n     * Emits a DisputeResolverUpdated event if successful.\n     * Emits a DisputeResolverUpdatePending event if the dispute resolver has requested an update for admin or assistant.\n     * Owner(s) of new addresses for admin, assistant must opt-in to the update.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address of the stored dispute resolver\n     * - Any address is zero address\n     * - Any address is not unique to this dispute resolver\n     * - Supplied clerk is not a zero address\n     * - Dispute resolver does not exist\n     * - EscalationResponsePeriod is invalid\n     * - No field has been updated or requested to be updated\n     *\n     * @param _disputeResolver - the fully populated dispute resolver struct\n     */\n    function updateDisputeResolver(\n        DisputeResolver memory _disputeResolver\n    ) external disputeResolversNotPaused nonReentrant {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Check for zero address\n        if (\n            _disputeResolver.admin == address(0) ||\n            _disputeResolver.assistant == address(0) ||\n            _disputeResolver.treasury == address(0)\n        ) {\n            revert InvalidAddress();\n        }\n        if (_disputeResolver.clerk != address(0)) revert ClerkDeprecated();\n\n        bool exists;\n        DisputeResolver storage disputeResolver;\n\n        // Check dispute resolver and dispute resolver Fees from disputeResolvers and disputeResolverFees mappings\n        (exists, disputeResolver, ) = fetchDisputeResolver(_disputeResolver.id);\n\n        // Dispute resolver must already exist\n        if (!exists) revert NoSuchDisputeResolver();\n\n        // Get message sender\n        address sender = msgSender();\n\n        // Check that caller is the admin address for this dispute resolver\n        if (disputeResolver.admin != sender) revert NotAdmin();\n\n        // Clean old dispute resolver pending update data if exists\n        delete lookups.pendingAddressUpdatesByDisputeResolver[_disputeResolver.id];\n\n        bool needsApproval;\n        (, DisputeResolver storage disputeResolverPendingUpdate) = fetchDisputeResolverPendingUpdate(\n            _disputeResolver.id\n        );\n\n        if (_disputeResolver.admin != disputeResolver.admin) {\n            preUpdateDisputeResolverCheck(_disputeResolver.id, _disputeResolver.admin, lookups);\n\n            // If admin address exists, admin address owner must approve the update to prevent front-running\n            disputeResolverPendingUpdate.admin = _disputeResolver.admin;\n            needsApproval = true;\n        }\n\n        if (_disputeResolver.assistant != disputeResolver.assistant) {\n            preUpdateDisputeResolverCheck(_disputeResolver.id, _disputeResolver.assistant, lookups);\n\n            // If assistant address exists, assistant address owner must approve the update to prevent front-running\n            disputeResolverPendingUpdate.assistant = _disputeResolver.assistant;\n            needsApproval = true;\n        }\n\n        bool updateApplied;\n\n        if (_disputeResolver.treasury != disputeResolver.treasury) {\n            // Update treasury\n            disputeResolver.treasury = _disputeResolver.treasury;\n\n            updateApplied = true;\n        }\n\n        if (_disputeResolver.escalationResponsePeriod != disputeResolver.escalationResponsePeriod) {\n            // Escalation period must be greater than zero and less than or equal to the max allowed\n            if (\n                _disputeResolver.escalationResponsePeriod == 0 ||\n                _disputeResolver.escalationResponsePeriod > protocolLimits().maxEscalationResponsePeriod\n            ) {\n                revert InvalidEscalationPeriod();\n            }\n\n            // Update escalation response period\n            disputeResolver.escalationResponsePeriod = _disputeResolver.escalationResponsePeriod;\n\n            updateApplied = true;\n        }\n\n        if (keccak256(bytes(_disputeResolver.metadataUri)) != keccak256(bytes(disputeResolver.metadataUri))) {\n            // Update metadata URI\n            disputeResolver.metadataUri = _disputeResolver.metadataUri;\n\n            updateApplied = true;\n        }\n\n        if (needsApproval) {\n            // Notify watchers of state change\n            emit DisputeResolverUpdatePending(_disputeResolver.id, disputeResolverPendingUpdate, sender);\n        }\n\n        if (updateApplied) {\n            // Notify watchers of state change\n            emit DisputeResolverUpdateApplied(\n                _disputeResolver.id,\n                disputeResolver,\n                disputeResolverPendingUpdate,\n                sender\n            );\n        }\n\n        if (!updateApplied && !needsApproval) revert NoUpdateApplied();\n    }\n\n    /**\n     * @notice Opt-in to a pending dispute resolver update\n     *\n     * Emits a DisputeResolverUpdateApplied event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolver region of protocol is paused\n     * - Addresses are not unique to this dispute resolver\n     * - Caller address is not pending update for the field being updated\n     * - No pending update exists for this dispute resolver\n     * - Dispute resolver tries to update the clerk\n     *\n     * @param _disputeResolverId - disputeResolver id\n     * @param _fieldsToUpdate - fields to update, see DisputeResolverUpdateFields enum\n     */\n    function optInToDisputeResolverUpdate(\n        uint256 _disputeResolverId,\n        DisputeResolverUpdateFields[] calldata _fieldsToUpdate\n    ) external disputeResolversNotPaused nonReentrant {\n        // Cache protocol lookups and sender for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n        address sender = msgSender();\n\n        // Get disputeResolver pending update\n        (bool exists, DisputeResolver storage disputeResolverPendingUpdate) = fetchDisputeResolverPendingUpdate(\n            _disputeResolverId\n        );\n\n        if (!exists) revert NoPendingUpdateForAccount();\n\n        bool updateApplied;\n\n        // Get storage location for disputeResolver\n        (, DisputeResolver storage disputeResolver, ) = fetchDisputeResolver(_disputeResolverId);\n\n        for (uint256 i = 0; i < _fieldsToUpdate.length; ) {\n            DisputeResolverUpdateFields role = _fieldsToUpdate[i];\n\n            if (role == DisputeResolverUpdateFields.Admin && disputeResolverPendingUpdate.admin != address(0)) {\n                // Approve admin update\n                if (disputeResolverPendingUpdate.admin != sender) revert UnauthorizedCallerUpdate();\n\n                preUpdateDisputeResolverCheck(_disputeResolverId, sender, lookups);\n\n                // Delete old disputeResolver id by admin mapping\n                delete lookups.disputeResolverIdByAdmin[disputeResolver.admin];\n\n                // Update admin\n                disputeResolver.admin = sender;\n\n                // Store new disputeResolver id by admin mapping\n                lookups.disputeResolverIdByAdmin[sender] = _disputeResolverId;\n\n                // Delete pending update admin\n                delete disputeResolverPendingUpdate.admin;\n\n                updateApplied = true;\n            } else if (\n                role == DisputeResolverUpdateFields.Assistant && disputeResolverPendingUpdate.assistant != address(0)\n            ) {\n                // Approve assistant update\n                if (disputeResolverPendingUpdate.assistant != sender) revert UnauthorizedCallerUpdate();\n\n                preUpdateDisputeResolverCheck(_disputeResolverId, sender, lookups);\n\n                // Delete old disputeResolver id by assistant mapping\n                delete lookups.disputeResolverIdByAssistant[disputeResolver.assistant];\n\n                // Update assistant\n                disputeResolver.assistant = sender;\n\n                // Store new disputeResolver id by assistant mapping\n                lookups.disputeResolverIdByAssistant[sender] = _disputeResolverId;\n\n                // Delete pending update assistant\n                delete disputeResolverPendingUpdate.assistant;\n\n                updateApplied = true;\n            } else if (role == DisputeResolverUpdateFields.Clerk) {\n                revert ClerkDeprecated();\n            }\n\n            unchecked {\n                i++;\n            }\n        }\n\n        if (updateApplied) {\n            // Notify watchers of state change\n            emit DisputeResolverUpdateApplied(\n                _disputeResolverId,\n                disputeResolver,\n                disputeResolverPendingUpdate,\n                sender\n            );\n        }\n    }\n\n    /**\n     * @notice Adds DisputeResolverFees to an existing dispute resolver.\n     *\n     * Emits a DisputeResolverFeesAdded event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address associated with the dispute resolver account\n     * - Dispute resolver does not exist\n     * - Number of DisputeResolverFee structs in array is zero\n     * - DisputeResolverFee array contains duplicates\n     * - Fee amount is a non-zero value. Protocol doesn't yet support fees for dispute resolvers\n     *\n     * @param _disputeResolverId - id of the dispute resolver\n     * @param _disputeResolverFees - list of fees dispute resolver charges per token type. Zero address is native currency. See {BosonTypes.DisputeResolverFee}\n     *                               feeAmount will be ignored because protocol doesn't yet support fees yet but DR still needs to provide array of fees to choose supported tokens\n     */\n    function addFeesToDisputeResolver(\n        uint256 _disputeResolverId,\n        DisputeResolverFee[] calldata _disputeResolverFees\n    ) external disputeResolversNotPaused nonReentrant {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        bool exists;\n        DisputeResolver storage disputeResolver;\n        DisputeResolverFee[] storage disputeResolverFees;\n\n        // Check dispute resolver and dispute resolver Fees from disputeResolvers and disputeResolverFees mappings\n        (exists, disputeResolver, disputeResolverFees) = fetchDisputeResolver(_disputeResolverId);\n\n        // Dispute resolver must already exist\n        if (!exists) revert NoSuchDisputeResolver();\n\n        // Get message sender\n        address sender = msgSender();\n\n        // Check that msg.sender is the admin address for this dispute resolver\n        if (disputeResolver.admin != sender) revert NotAdmin();\n\n        // At least one fee must be specified\n        if (_disputeResolverFees.length == 0) revert InexistentDisputeResolverFees();\n\n        // Set dispute resolver fees. Must loop because calldata structs cannot be converted to storage structs\n        for (uint256 i = 0; i < _disputeResolverFees.length; ) {\n            if (lookups.disputeResolverFeeTokenIndex[_disputeResolverId][_disputeResolverFees[i].tokenAddress] != 0)\n                revert DuplicateDisputeResolverFees();\n\n            // Protocol doesn't yet support DR fees\n            if (_disputeResolverFees[i].feeAmount != 0) revert FeeAmountNotYetSupported();\n\n            disputeResolverFees.push(_disputeResolverFees[i]);\n            lookups.disputeResolverFeeTokenIndex[_disputeResolverId][\n                _disputeResolverFees[i].tokenAddress\n            ] = disputeResolverFees.length; // Set index mapping. Should be index in disputeResolverFees array + 1\n\n            unchecked {\n                i++;\n            }\n        }\n\n        emit DisputeResolverFeesAdded(_disputeResolverId, _disputeResolverFees, sender);\n    }\n\n    /**\n     * @notice Removes DisputeResolverFees from  an existing dispute resolver.\n     *\n     * Emits a DisputeResolverFeesRemoved event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address associated with the dispute resolver account\n     * - Dispute resolver does not exist\n     * - Number of DisputeResolverFee structs in array is zero\n     * - DisputeResolverFee does not exist for the dispute resolver\n     *\n     * @param _disputeResolverId - id of the dispute resolver\n     * @param _feeTokenAddresses - list of addresses of dispute resolver fee tokens to remove\n     */\n    function removeFeesFromDisputeResolver(\n        uint256 _disputeResolverId,\n        address[] calldata _feeTokenAddresses\n    ) external disputeResolversNotPaused nonReentrant {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        bool exists;\n        DisputeResolver storage disputeResolver;\n        DisputeResolverFee[] storage disputeResolverFees;\n\n        // Check dispute resolver and dispute resolver fees from disputeResolvers and disputeResolverFees mappings\n        (exists, disputeResolver, disputeResolverFees) = fetchDisputeResolver(_disputeResolverId);\n\n        // Dispute resolver must already exist\n        if (!exists) revert NoSuchDisputeResolver();\n\n        // Get message sender\n        address sender = msgSender();\n\n        // Check that msg.sender is the admin address for this dispute resolver\n        if (disputeResolver.admin != sender) revert NotAdmin();\n\n        // At least one fee must be specified and\n        if (_feeTokenAddresses.length == 0) revert InexistentDisputeResolverFees();\n\n        // Set dispute resolver fees. Must loop because calldata structs cannot be converted to storage structs\n        for (uint256 i = 0; i < _feeTokenAddresses.length; ) {\n            if (lookups.disputeResolverFeeTokenIndex[_disputeResolverId][_feeTokenAddresses[i]] == 0)\n                revert DisputeResolverFeeNotFound();\n\n            uint256 disputeResolverFeeArrayIndex = lookups.disputeResolverFeeTokenIndex[_disputeResolverId][\n                _feeTokenAddresses[i]\n            ] - 1; //Get the index in the DisputeResolverFees array, which is 1 less than the disputeResolverFeeTokenIndex index\n\n            uint256 lastTokenIndex = disputeResolverFees.length - 1;\n            if (disputeResolverFeeArrayIndex != lastTokenIndex) {\n                // if index == len - 1 then only pop and delete are needed\n                // Need to fill gap caused by delete if more than one element in storage array\n                DisputeResolverFee memory disputeResolverFeeToMove = disputeResolverFees[lastTokenIndex];\n                disputeResolverFees[disputeResolverFeeArrayIndex] = disputeResolverFeeToMove; // Copy the last DisputeResolverFee struct in the array to this index to fill the gap\n                lookups.disputeResolverFeeTokenIndex[_disputeResolverId][disputeResolverFeeToMove.tokenAddress] =\n                    disputeResolverFeeArrayIndex +\n                    1; // Reset index mapping. Should be index in disputeResolverFees array + 1\n            }\n            disputeResolverFees.pop(); // Delete last DisputeResolverFee struct in the array, which was just moved to fill the gap\n            delete lookups.disputeResolverFeeTokenIndex[_disputeResolverId][_feeTokenAddresses[i]]; // Delete from index mapping\n\n            unchecked {\n                i++;\n            }\n        }\n\n        emit DisputeResolverFeesRemoved(_disputeResolverId, _feeTokenAddresses, sender);\n    }\n\n    /**\n     * @notice Adds seller ids to set of ids allowed to choose the given dispute resolver for an offer.\n     *\n     * Emits an AllowedSellersAdded event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address associated with the dispute resolver account\n     * - Dispute resolver does not exist\n     * - Number of seller ids in array is zero\n     * - Some seller does not exist\n     * - Seller id is already approved\n     *\n     * @param _disputeResolverId - id of the dispute resolver\n     * @param _sellerAllowList - list of seller ids to add to allowed list\n     */\n    function addSellersToAllowList(\n        uint256 _disputeResolverId,\n        uint256[] calldata _sellerAllowList\n    ) external disputeResolversNotPaused nonReentrant {\n        // At least one seller id must be specified\n        if (_sellerAllowList.length == 0) revert InexistentAllowedSellersList();\n\n        bool exists;\n        DisputeResolver storage disputeResolver;\n\n        // Check dispute Resolver from disputeResolvers\n        (exists, disputeResolver, ) = fetchDisputeResolver(_disputeResolverId);\n\n        // Dispute resolver must already exist\n        if (!exists) revert NoSuchDisputeResolver();\n\n        // Get message sender\n        address sender = msgSender();\n\n        // Check that msg.sender is the admin address for this dispute resolver\n        if (disputeResolver.admin != sender) revert NotAdmin();\n\n        storeSellerAllowList(_disputeResolverId, _sellerAllowList);\n\n        emit AllowedSellersAdded(_disputeResolverId, _sellerAllowList, sender);\n    }\n\n    /**\n     * @notice Removes seller ids from set of ids allowed to choose the given dispute resolver for an offer.\n     *\n     * Emits an AllowedSellersRemoved event if successful.\n     *\n     * Reverts if:\n     * - The dispute resolvers region of protocol is paused\n     * - Caller is not the admin address associated with the dispute resolver account\n     * - Dispute resolver does not exist\n     * - Number of seller ids structs in array is zero\n     * - Seller id is not approved\n     *\n     * @param _disputeResolverId - id of the dispute resolver\n     * @param _sellerAllowList - list of seller ids to remove from allowed list\n     */\n    function removeSellersFromAllowList(\n        uint256 _disputeResolverId,\n        uint256[] calldata _sellerAllowList\n    ) external disputeResolversNotPaused nonReentrant {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // At least one seller id must be specified\n        if (_sellerAllowList.length == 0) revert InexistentAllowedSellersList();\n\n        bool exists;\n        DisputeResolver storage disputeResolver;\n\n        // Check dispute resolver from disputeResolvers\n        (exists, disputeResolver, ) = fetchDisputeResolver(_disputeResolverId);\n\n        // Dispute resolver must already exist\n        if (!exists) revert NoSuchDisputeResolver();\n\n        // Get message sender\n        address sender = msgSender();\n\n        // Check that msg.sender is the admin address for this dispute resolver\n        if (disputeResolver.admin != sender) revert NotAdmin();\n\n        for (uint256 i = 0; i < _sellerAllowList.length; ) {\n            uint256 sellerToRemoveIndex = lookups.allowedSellerIndex[_disputeResolverId][_sellerAllowList[i]];\n            if (sellerToRemoveIndex == 0) revert SellerNotApproved();\n\n            // remove index mapping\n            delete lookups.allowedSellerIndex[_disputeResolverId][_sellerAllowList[i]];\n\n            // reduce for 1 to get actual index value\n            sellerToRemoveIndex--;\n\n            uint256 lastIndex = lookups.allowedSellers[_disputeResolverId].length - 1; // since allowedSellerIndex > 0, length at this point cannot be 0 therefore we don't worry about overflow\n\n            // if index to remove is not the last index we put the last element in its place\n            if (sellerToRemoveIndex != lastIndex) {\n                uint256 lastSellerId = lookups.allowedSellers[_disputeResolverId][lastIndex];\n                lookups.allowedSellers[_disputeResolverId][sellerToRemoveIndex] = lastSellerId;\n                lookups.allowedSellerIndex[_disputeResolverId][lastSellerId] = sellerToRemoveIndex + 1;\n            }\n\n            // remove last element\n            lookups.allowedSellers[_disputeResolverId].pop();\n\n            unchecked {\n                i++;\n            }\n        }\n\n        emit AllowedSellersRemoved(_disputeResolverId, _sellerAllowList, sender);\n    }\n\n    /**\n     * @notice Gets the details about a dispute resolver.\n     *\n     * @param _disputeResolverId - the id of the dispute resolver to check\n     * @return exists - the dispute resolver was found\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     * @return sellerAllowList - list of sellers that are allowed to choose this dispute resolver\n     */\n    function getDisputeResolver(\n        uint256 _disputeResolverId\n    )\n        public\n        view\n        returns (\n            bool exists,\n            DisputeResolver memory disputeResolver,\n            DisputeResolverFee[] memory disputeResolverFees,\n            uint256[] memory sellerAllowList\n        )\n    {\n        (exists, disputeResolver, disputeResolverFees) = fetchDisputeResolver(_disputeResolverId);\n        if (exists) {\n            disputeResolver.clerk = address(0);\n            sellerAllowList = protocolLookups().allowedSellers[_disputeResolverId];\n        }\n    }\n\n    /**\n     * @notice Gets the details about a dispute resolver by an address associated with that dispute resolver: assistant, or admin address.\n     *\n     * @param _associatedAddress - the address associated with the dispute resolver. Must be an assistant or admin address.\n     * @return exists - the dispute resolver was found\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     * @return sellerAllowList - list of sellers that are allowed to chose this dispute resolver\n     */\n    function getDisputeResolverByAddress(\n        address _associatedAddress\n    )\n        external\n        view\n        returns (\n            bool exists,\n            DisputeResolver memory disputeResolver,\n            DisputeResolverFee[] memory disputeResolverFees,\n            uint256[] memory sellerAllowList\n        )\n    {\n        uint256 disputeResolverId;\n\n        (exists, disputeResolverId) = getDisputeResolverIdByAssistant(_associatedAddress);\n\n        if (exists) {\n            return getDisputeResolver(disputeResolverId);\n        }\n\n        (exists, disputeResolverId) = getDisputeResolverIdByAdmin(_associatedAddress);\n\n        if (exists) {\n            return getDisputeResolver(disputeResolverId);\n        }\n    }\n\n    /**\n     * @notice Checks whether given sellers are allowed to choose the given dispute resolver.\n     *\n     * @param _disputeResolverId - id of dispute resolver to check\n     * @param _sellerIds - list of seller ids to check\n     * @return sellerAllowed - array with indicator (true/false) if seller is allowed to choose the dispute resolver. Index in this array corresponds to indices of the incoming _sellerIds\n     */\n    function areSellersAllowed(\n        uint256 _disputeResolverId,\n        uint256[] calldata _sellerIds\n    ) external view returns (bool[] memory sellerAllowed) {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        sellerAllowed = new bool[](_sellerIds.length);\n\n        (bool exists, , ) = fetchDisputeResolver(_disputeResolverId);\n\n        // We populate sellerAllowed only if id really belongs to DR, otherwise return array filled with false\n        if (exists) {\n            if (lookups.allowedSellers[_disputeResolverId].length == 0) {\n                // DR allows everyone, just make sure ids really belong to the sellers\n                for (uint256 i = 0; i < _sellerIds.length; ) {\n                    (exists, , ) = fetchSeller(_sellerIds[i]);\n                    sellerAllowed[i] = exists;\n\n                    unchecked {\n                        i++;\n                    }\n                }\n            } else {\n                // DR is selective. Check for every seller if they are allowed for given _disputeResolverId\n                for (uint256 i = 0; i < _sellerIds.length; ) {\n                    sellerAllowed[i] = lookups.allowedSellerIndex[_disputeResolverId][_sellerIds[i]] > 0; // true if on the list, false otherwise\n\n                    unchecked {\n                        i++;\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * @notice Stores DisputeResolver struct in storage.\n     *\n     * @param _disputeResolver - the fully populated struct with dispute resolver id set\n     */\n    function storeDisputeResolver(DisputeResolver memory _disputeResolver) internal {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Get storage location for dispute resolver\n        (, DisputeResolver storage disputeResolver, ) = fetchDisputeResolver(_disputeResolver.id);\n\n        // Set dispute resolver props individually since memory structs can't be copied to storage\n        disputeResolver.id = _disputeResolver.id;\n        disputeResolver.escalationResponsePeriod = _disputeResolver.escalationResponsePeriod;\n        disputeResolver.assistant = _disputeResolver.assistant;\n        disputeResolver.admin = _disputeResolver.admin;\n        disputeResolver.treasury = _disputeResolver.treasury;\n        disputeResolver.metadataUri = _disputeResolver.metadataUri;\n        disputeResolver.active = _disputeResolver.active;\n\n        // Map the dispute resolver's addresses to the dispute resolver id.\n        lookups.disputeResolverIdByAssistant[_disputeResolver.assistant] = _disputeResolver.id;\n        lookups.disputeResolverIdByAdmin[_disputeResolver.admin] = _disputeResolver.id;\n    }\n\n    /**\n     * @notice Stores seller id to allowed list mapping in storage.\n     *\n     * Reverts if:\n     * - Some seller does not exist\n     * - Some seller id is already approved\n     *\n     * @param _disputeResolverId - id of dispute resolver that is giving the permission\n     * @param _sellerAllowList - list of seller ids added to allow list\n     */\n    function storeSellerAllowList(uint256 _disputeResolverId, uint256[] calldata _sellerAllowList) internal {\n        // Cache protocol lookups for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Loop over incoming seller ids and store them to the mapping\n        for (uint256 i = 0; i < _sellerAllowList.length; ) {\n            uint256 sellerId = _sellerAllowList[i];\n            // Check Seller exists in sellers mapping\n            (bool exists, , ) = fetchSeller(sellerId);\n\n            // Seller must already exist\n            if (!exists) revert NoSuchSeller();\n\n            // Seller should not be approved already\n            if (lookups.allowedSellerIndex[_disputeResolverId][sellerId] != 0) revert SellerAlreadyApproved();\n\n            // Update the mappings\n            lookups.allowedSellers[_disputeResolverId].push(sellerId);\n            lookups.allowedSellerIndex[_disputeResolverId][sellerId] = lookups\n                .allowedSellers[_disputeResolverId]\n                .length; //Set index mapping. Should be index in allowedSellers array + 1\n\n            unchecked {\n                i++;\n            }\n        }\n    }\n\n    /**\n     * @notice Pre update dispute resolver checks\n     *\n     * Reverts if:\n     *   - Address has already been used by another dispute resolver as assistant or admin\n     *\n     * @param _disputeResolverId - the id of the disputeResolver to check\n     * @param _role - the address to check\n     * @param _lookups - the lookups struct\n     */\n    function preUpdateDisputeResolverCheck(\n        uint256 _disputeResolverId,\n        address _role,\n        ProtocolLib.ProtocolLookups storage _lookups\n    ) internal view {\n        // Check that the role is unique to one dispute resolver id across all roles -- not used or is used by this dispute resolver id.\n        uint256 check1 = _lookups.disputeResolverIdByAssistant[_role];\n        uint256 check2 = _lookups.disputeResolverIdByAdmin[_role];\n\n        if ((check1 != 0 && check1 != _disputeResolverId) || (check2 != 0 && check2 != _disputeResolverId)) {\n            revert DisputeResolverAddressMustBeUnique();\n        }\n    }\n\n    /**\n     * @notice Fetches a given dispute resolver pending update from storage by id\n     *\n     * @param _disputeResolverId - the id of the dispute resolver\n     * @return exists - whether the dispute resolver pending update exists\n     * @return disputeResolverPendingUpdate - the dispute resolver pending update details. See {BosonTypes.DisputeResolver}\n     */\n    function fetchDisputeResolverPendingUpdate(\n        uint256 _disputeResolverId\n    ) internal view returns (bool exists, DisputeResolver storage disputeResolverPendingUpdate) {\n        // Cache protocol entities for reference\n        ProtocolLib.ProtocolLookups storage lookups = protocolLookups();\n\n        // Get the dispute resolver pending update slot\n        disputeResolverPendingUpdate = lookups.pendingAddressUpdatesByDisputeResolver[_disputeResolverId];\n\n        // Determine existence\n        exists =\n            disputeResolverPendingUpdate.admin != address(0) ||\n            disputeResolverPendingUpdate.assistant != address(0);\n    }\n}\n"
      },
      "contracts/protocol/libs/EIP712Lib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport \"../../domain/BosonConstants.sol\";\nimport { BosonErrors } from \"../../domain/BosonErrors.sol\";\nimport { ProtocolLib } from \"../libs/ProtocolLib.sol\";\n\n/**\n * @title EIP712Lib\n *\n * @dev Provides the domain separator and chain id.\n */\nlibrary EIP712Lib {\n    /**\n     * @notice Generates the domain separator hash.\n     * @dev Using the chainId as the salt enables the client to be active on one chain\n     * while a metatx is signed for a contract on another chain. That could happen if the client is,\n     * for instance, a metaverse scene that runs on one chain while the contracts it interacts with are deployed on another chain.\n     *\n     * @param _name - the name of the protocol\n     * @param _version -  The version of the protocol\n     * @return the domain separator hash\n     */\n    function buildDomainSeparator(string memory _name, string memory _version) internal view returns (bytes32) {\n        return\n            keccak256(\n                abi.encode(\n                    EIP712_DOMAIN_TYPEHASH,\n                    keccak256(bytes(_name)),\n                    keccak256(bytes(_version)),\n                    address(this),\n                    block.chainid\n                )\n            );\n    }\n\n    /**\n     * @notice Recovers the Signer from the Signature components.\n     *\n     * Reverts if:\n     * - Signer is the zero address\n     *\n     * @param _user  - the sender of the transaction\n     * @param _hashedMetaTx - hashed meta transaction\n     * @param _sigR - r part of the signer's signature\n     * @param _sigS - s part of the signer's signature\n     * @param _sigV - v part of the signer's signature\n     * @return true if signer is same as _user parameter\n     */\n    function verify(\n        address _user,\n        bytes32 _hashedMetaTx,\n        bytes32 _sigR,\n        bytes32 _sigS,\n        uint8 _sigV\n    ) internal returns (bool) {\n        // Ensure signature is unique\n        // See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/04695aecbd4d17dddfd55de766d10e3805d6f42f/contracts/cryptography/ECDSA.sol#63\n        if (\n            uint256(_sigS) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 ||\n            (_sigV != 27 && _sigV != 28)\n        ) revert BosonErrors.InvalidSignature();\n\n        address signer = ecrecover(toTypedMessageHash(_hashedMetaTx), _sigV, _sigR, _sigS);\n        if (signer == address(0)) revert BosonErrors.InvalidSignature();\n        return signer == _user;\n    }\n\n    /**\n     * @notice Gets the domain separator from storage if matches with the chain id and diamond address, else, build new domain separator.\n     *\n     * @return the domain separator\n     */\n    function getDomainSeparator() private returns (bytes32) {\n        ProtocolLib.ProtocolMetaTxInfo storage pmti = ProtocolLib.protocolMetaTxInfo();\n        uint256 cachedChainId = pmti.cachedChainId;\n\n        if (block.chainid == cachedChainId) {\n            return pmti.domainSeparator;\n        } else {\n            bytes32 domainSeparator = buildDomainSeparator(PROTOCOL_NAME, PROTOCOL_VERSION);\n            pmti.domainSeparator = domainSeparator;\n            pmti.cachedChainId = block.chainid;\n\n            return domainSeparator;\n        }\n    }\n\n    /**\n     * @notice Generates EIP712 compatible message hash.\n     *\n     * @dev Accepts message hash and returns hash message in EIP712 compatible form\n     * so that it can be used to recover signer from signature signed using EIP712 formatted data\n     * https://eips.ethereum.org/EIPS/eip-712\n     * \"\\\\x19\" makes the encoding deterministic\n     * \"\\\\x01\" is the version byte to make it compatible to EIP-191\n     *\n     * @param _messageHash  - the message hash\n     * @return the EIP712 compatible message hash\n     */\n    function toTypedMessageHash(bytes32 _messageHash) internal returns (bytes32) {\n        return keccak256(abi.encodePacked(\"\\x19\\x01\", getDomainSeparator(), _messageHash));\n    }\n\n    /**\n     * @notice Gets the current message sender address from storage.\n     *\n     * @return the the current message sender address from storage\n     */\n    function getCurrentSenderAddress() internal view returns (address) {\n        return ProtocolLib.protocolMetaTxInfo().currentSenderAddress;\n    }\n\n    /**\n     * @notice Returns the message sender address.\n     *\n     * @dev Could be msg.sender or the message sender address from storage (in case of meta transaction).\n     *\n     * @return the message sender address\n     */\n    function msgSender() internal view returns (address) {\n        bool isItAMetaTransaction = ProtocolLib.protocolMetaTxInfo().isMetaTransaction;\n\n        // Get sender from the storage if this is a meta transaction\n        if (isItAMetaTransaction) {\n            address sender = getCurrentSenderAddress();\n            if (sender == address(0)) revert BosonErrors.InvalidAddress();\n\n            return sender;\n        } else {\n            return msg.sender;\n        }\n    }\n}\n"
      },
      "contracts/protocol/libs/ProtocolLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.22;\n\nimport { BosonTypes } from \"../../domain/BosonTypes.sol\";\n\n/**\n * @title ProtocolLib\n *\n * @notice Provides access to the protocol addresses, limits, entities, fees, counters, initializers and  metaTransactions slots for Facets.\n */\nlibrary ProtocolLib {\n    bytes32 internal constant PROTOCOL_ADDRESSES_POSITION = keccak256(\"boson.protocol.addresses\");\n    bytes32 internal constant PROTOCOL_LIMITS_POSITION = keccak256(\"boson.protocol.limits\");\n    bytes32 internal constant PROTOCOL_ENTITIES_POSITION = keccak256(\"boson.protocol.entities\");\n    bytes32 internal constant PROTOCOL_LOOKUPS_POSITION = keccak256(\"boson.protocol.lookups\");\n    bytes32 internal constant PROTOCOL_FEES_POSITION = keccak256(\"boson.protocol.fees\");\n    bytes32 internal constant PROTOCOL_COUNTERS_POSITION = keccak256(\"boson.protocol.counters\");\n    bytes32 internal constant PROTOCOL_STATUS_POSITION = keccak256(\"boson.protocol.initializers\");\n    bytes32 internal constant PROTOCOL_META_TX_POSITION = keccak256(\"boson.protocol.metaTransactions\");\n\n    // Protocol addresses storage\n    struct ProtocolAddresses {\n        // Address of the Boson Protocol treasury\n        address payable treasury;\n        // Address of the Boson Token (ERC-20 contract)\n        address payable token;\n        // Address of the Boson Protocol Voucher beacon\n        address voucherBeacon;\n        // Address of the Boson Beacon proxy implementation\n        address beaconProxy;\n        // Address of the Boson Price Discovery\n        address priceDiscovery;\n    }\n\n    // Protocol limits storage\n    struct ProtocolLimits {\n        // limit on the resolution period that a seller can specify\n        uint256 maxResolutionPeriod;\n        // limit on the escalation response period that a dispute resolver can specify\n        uint256 maxEscalationResponsePeriod;\n        // lower limit for dispute period\n        uint256 minDisputePeriod;\n        // limit how many exchanges can be processed in single batch transaction\n        uint16 maxExchangesPerBatch;\n        // limit how many offers can be added to the group\n        uint16 maxOffersPerGroup;\n        // limit how many offers can be added to the bundle\n        uint16 maxOffersPerBundle;\n        // limit how many twins can be added to the bundle\n        uint16 maxTwinsPerBundle;\n        // limit how many offers can be processed in single batch transaction\n        uint16 maxOffersPerBatch;\n        // limit how many different tokens can be withdrawn in a single transaction\n        uint16 maxTokensPerWithdrawal;\n        // limit how many dispute resolver fee structs can be processed in a single transaction\n        uint16 maxFeesPerDisputeResolver;\n        // limit how many disputes can be processed in single batch transaction\n        uint16 maxDisputesPerBatch;\n        // limit how many sellers can be added to or removed from an allow list in a single transaction\n        uint16 maxAllowedSellers;\n        // limit the sum of (protocol fee percentage + agent fee percentage) of an offer fee\n        uint16 maxTotalOfferFeePercentage;\n        // limit the max royalty percentage that can be set by the seller\n        uint16 maxRoyaltyPercentage;\n        // limit the max number of vouchers that can be preminted in a single transaction\n        uint256 maxPremintedVouchers;\n        // lower limit for resolution period\n        uint256 minResolutionPeriod;\n    }\n\n    // Protocol fees storage\n    struct ProtocolFees {\n        // Percentage that will be taken as a fee from the net of a Boson Protocol exchange\n        uint256 percentage; // 1.75% = 175, 100% = 10000\n        // Flat fee taken for exchanges in $BOSON\n        uint256 flatBoson;\n        // buyer escalation deposit percentage\n        uint256 buyerEscalationDepositPercentage;\n    }\n\n    // Protocol entities storage\n    struct ProtocolEntities {\n        // offer id => offer\n        mapping(uint256 => BosonTypes.Offer) offers;\n        // offer id => offer dates\n        mapping(uint256 => BosonTypes.OfferDates) offerDates;\n        // offer id => offer fees\n        mapping(uint256 => BosonTypes.OfferFees) offerFees;\n        // offer id => offer durations\n        mapping(uint256 => BosonTypes.OfferDurations) offerDurations;\n        // offer id => dispute resolution terms\n        mapping(uint256 => BosonTypes.DisputeResolutionTerms) disputeResolutionTerms;\n        // exchange id => exchange\n        mapping(uint256 => BosonTypes.Exchange) exchanges;\n        // exchange id => voucher\n        mapping(uint256 => BosonTypes.Voucher) vouchers;\n        // exchange id => dispute\n        mapping(uint256 => BosonTypes.Dispute) disputes;\n        // exchange id => dispute dates\n        mapping(uint256 => BosonTypes.DisputeDates) disputeDates;\n        // seller id => seller\n        mapping(uint256 => BosonTypes.Seller) sellers;\n        // buyer id => buyer\n        mapping(uint256 => BosonTypes.Buyer) buyers;\n        // dispute resolver id => dispute resolver\n        mapping(uint256 => BosonTypes.DisputeResolver) disputeResolvers;\n        // dispute resolver id => dispute resolver fee array\n        mapping(uint256 => BosonTypes.DisputeResolverFee[]) disputeResolverFees;\n        // agent id => agent\n        mapping(uint256 => BosonTypes.Agent) agents;\n        // group id => group\n        mapping(uint256 => BosonTypes.Group) groups;\n        // group id => condition\n        mapping(uint256 => BosonTypes.Condition) conditions;\n        // bundle id => bundle\n        mapping(uint256 => BosonTypes.Bundle) bundles;\n        // twin id => twin\n        mapping(uint256 => BosonTypes.Twin) twins;\n        // entity id => auth token\n        mapping(uint256 => BosonTypes.AuthToken) authTokens;\n        // exchange id => sequential commit info\n        mapping(uint256 => BosonTypes.ExchangeCosts[]) exchangeCosts;\n        // entity id => royalty recipient account\n        mapping(uint256 => BosonTypes.RoyaltyRecipient) royaltyRecipients;\n    }\n\n    // Protocol lookups storage\n    struct ProtocolLookups {\n        // offer id => exchange ids\n        mapping(uint256 => uint256[]) exchangeIdsByOffer;\n        // offer id => bundle id\n        mapping(uint256 => uint256) bundleIdByOffer;\n        // twin id => bundle id\n        mapping(uint256 => uint256) bundleIdByTwin;\n        // offer id => group id\n        mapping(uint256 => uint256) groupIdByOffer;\n        // offer id => agent id\n        mapping(uint256 => uint256) agentIdByOffer;\n        // seller assistant address => sellerId\n        mapping(address => uint256) sellerIdByAssistant;\n        // seller admin address => sellerId\n        mapping(address => uint256) sellerIdByAdmin;\n        // seller clerk address => sellerId\n        // @deprecated sellerIdByClerk is no longer used. Keeping it for backwards compatibility.\n        mapping(address => uint256) sellerIdByClerk;\n        // buyer wallet address => buyerId\n        mapping(address => uint256) buyerIdByWallet;\n        // dispute resolver assistant address => disputeResolverId\n        mapping(address => uint256) disputeResolverIdByAssistant;\n        // dispute resolver admin address => disputeResolverId\n        mapping(address => uint256) disputeResolverIdByAdmin;\n        // dispute resolver clerk address => disputeResolverId\n        // @deprecated disputeResolverIdByClerk is no longer used. Keeping it for backwards compatibility.\n        mapping(address => uint256) disputeResolverIdByClerk;\n        // dispute resolver id to fee token address => index of the token address\n        mapping(uint256 => mapping(address => uint256)) disputeResolverFeeTokenIndex;\n        // agent wallet address => agentId\n        mapping(address => uint256) agentIdByWallet;\n        // account id => token address => amount\n        mapping(uint256 => mapping(address => uint256)) availableFunds;\n        // account id => all tokens with balance > 0\n        mapping(uint256 => address[]) tokenList;\n        // account id => token address => index on token addresses list\n        mapping(uint256 => mapping(address => uint256)) tokenIndexByAccount;\n        // seller id => cloneAddress\n        mapping(uint256 => address) cloneAddress;\n        // buyer id => number of active vouchers\n        mapping(uint256 => uint256) voucherCount;\n        // buyer address => groupId => commit count (addresses that have committed to conditional offers)\n        mapping(address => mapping(uint256 => uint256)) conditionalCommitsByAddress;\n        // AuthTokenType => Auth NFT contract address.\n        mapping(BosonTypes.AuthTokenType => address) authTokenContracts;\n        // AuthTokenType => tokenId => sellerId\n        mapping(BosonTypes.AuthTokenType => mapping(uint256 => uint256)) sellerIdByAuthToken;\n        // seller id => token address (only ERC721) => start and end of token ids range\n        mapping(uint256 => mapping(address => BosonTypes.TokenRange[])) twinRangesBySeller;\n        // seller id => token address (only ERC721) => twin ids\n        // @deprecated twinIdsByTokenAddressAndBySeller is no longer used. Keeping it for backwards compatibility.\n        mapping(uint256 => mapping(address => uint256[])) twinIdsByTokenAddressAndBySeller;\n        // exchange id => BosonTypes.TwinReceipt\n        mapping(uint256 => BosonTypes.TwinReceipt[]) twinReceiptsByExchange;\n        // dispute resolver id => list of allowed sellers\n        mapping(uint256 => uint256[]) allowedSellers;\n        // dispute resolver id => seller id => index of allowed seller in allowedSellers\n        mapping(uint256 => mapping(uint256 => uint256)) allowedSellerIndex;\n        // exchange id => condition\n        mapping(uint256 => BosonTypes.Condition) exchangeCondition;\n        // groupId => offerId => index on Group.offerIds array\n        mapping(uint256 => mapping(uint256 => uint256)) offerIdIndexByGroup;\n        // seller id => Seller\n        mapping(uint256 => BosonTypes.Seller) pendingAddressUpdatesBySeller;\n        // seller id => AuthToken\n        mapping(uint256 => BosonTypes.AuthToken) pendingAuthTokenUpdatesBySeller;\n        // dispute resolver id => DisputeResolver\n        mapping(uint256 => BosonTypes.DisputeResolver) pendingAddressUpdatesByDisputeResolver;\n        // twin id => range id\n        mapping(uint256 => uint256) rangeIdByTwin;\n        // tokenId => groupId =>  commit count (count how many times a token has been used as gate for this group)\n        mapping(uint256 => mapping(uint256 => uint256)) conditionalCommitsByTokenId;\n        // seller id => collections\n        mapping(uint256 => BosonTypes.Collection[]) additionalCollections;\n        // seller id => seller salt used to create collections\n        mapping(uint256 => bytes32) sellerSalt;\n        // seller salt => is used\n        mapping(bytes32 => bool) isUsedSellerSalt;\n        // seller id => royalty recipients info\n        mapping(uint256 => BosonTypes.RoyaltyRecipientInfo[]) royaltyRecipientsBySeller;\n        // seller id => royalty recipient => index of royalty recipient in royaltyRecipientsBySeller\n        mapping(uint256 => mapping(address => uint256)) royaltyRecipientIndexBySellerAndRecipient;\n        // royalty recipient wallet address => agentId\n        mapping(address => uint256) royaltyRecipientIdByWallet;\n    }\n\n    // Incrementing id counters\n    struct ProtocolCounters {\n        // Next account id\n        uint256 nextAccountId;\n        // Next offer id\n        uint256 nextOfferId;\n        // Next exchange id\n        uint256 nextExchangeId;\n        // Next twin id\n        uint256 nextTwinId;\n        // Next group id\n        uint256 nextGroupId;\n        // Next twin id\n        uint256 nextBundleId;\n    }\n\n    // Storage related to Meta Transactions\n    struct ProtocolMetaTxInfo {\n        // The current sender address associated with the transaction\n        address currentSenderAddress;\n        // A flag that tells us whether the current transaction is a meta-transaction or a regular transaction.\n        bool isMetaTransaction;\n        // The domain Separator of the protocol\n        bytes32 domainSeparator;\n        // address => nonce => nonce used indicator\n        mapping(address => mapping(uint256 => bool)) usedNonce;\n        // The cached chain id\n        uint256 cachedChainId;\n        // map function name to input type\n        mapping(string => BosonTypes.MetaTxInputType) inputType;\n        // map input type => hash info\n        mapping(BosonTypes.MetaTxInputType => BosonTypes.HashInfo) hashInfo;\n        // Can function be executed using meta transactions\n        mapping(bytes32 => bool) isAllowlisted;\n    }\n\n    // Individual facet initialization states\n    struct ProtocolStatus {\n        // the current pause scenario, a sum of PausableRegions as powers of two\n        uint256 pauseScenario;\n        // reentrancy status\n        uint256 reentrancyStatus;\n        // interface id => initialized?\n        mapping(bytes4 => bool) initializedInterfaces;\n        // version => initialized?\n        mapping(bytes32 => bool) initializedVersions;\n        // Current protocol version\n        bytes32 version;\n        // Incoming voucher id\n        uint256 incomingVoucherId;\n        // Incoming voucher clone address\n        address incomingVoucherCloneAddress;\n    }\n\n    /**\n     * @dev Gets the protocol addresses slot\n     *\n     * @return pa - the protocol addresses slot\n     */\n    function protocolAddresses() internal pure returns (ProtocolAddresses storage pa) {\n        bytes32 position = PROTOCOL_ADDRESSES_POSITION;\n        assembly {\n            pa.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol limits slot\n     *\n     * @return pl - the protocol limits slot\n     */\n    function protocolLimits() internal pure returns (ProtocolLimits storage pl) {\n        bytes32 position = PROTOCOL_LIMITS_POSITION;\n        assembly {\n            pl.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol entities slot\n     *\n     * @return pe - the protocol entities slot\n     */\n    function protocolEntities() internal pure returns (ProtocolEntities storage pe) {\n        bytes32 position = PROTOCOL_ENTITIES_POSITION;\n        assembly {\n            pe.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol lookups slot\n     *\n     * @return pl - the protocol lookups slot\n     */\n    function protocolLookups() internal pure returns (ProtocolLookups storage pl) {\n        bytes32 position = PROTOCOL_LOOKUPS_POSITION;\n        assembly {\n            pl.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol fees slot\n     *\n     * @return pf - the protocol fees slot\n     */\n    function protocolFees() internal pure returns (ProtocolFees storage pf) {\n        bytes32 position = PROTOCOL_FEES_POSITION;\n        assembly {\n            pf.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol counters slot\n     *\n     * @return pc - the protocol counters slot\n     */\n    function protocolCounters() internal pure returns (ProtocolCounters storage pc) {\n        bytes32 position = PROTOCOL_COUNTERS_POSITION;\n        assembly {\n            pc.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol meta-transactions storage slot\n     *\n     * @return pmti - the protocol meta-transactions storage slot\n     */\n    function protocolMetaTxInfo() internal pure returns (ProtocolMetaTxInfo storage pmti) {\n        bytes32 position = PROTOCOL_META_TX_POSITION;\n        assembly {\n            pmti.slot := position\n        }\n    }\n\n    /**\n     * @notice Gets the protocol status slot\n     *\n     * @return ps - the the protocol status slot\n     */\n    function protocolStatus() internal pure returns (ProtocolStatus storage ps) {\n        bytes32 position = PROTOCOL_STATUS_POSITION;\n        assembly {\n            ps.slot := position\n        }\n    }\n}\n"
      }
    },
    "settings": {
      "viaIR": false,
      "optimizer": {
        "enabled": true,
        "runs": 200,
        "details": {
          "yul": true
        }
      },
      "evmVersion": "london",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}