ETH Price: $1,999.18 (-1.20%)
Gas: 0.21 Gwei

Transaction Decoder

Block:
16666425 at Feb-20-2023 01:14:35 AM +UTC
Transaction Fee:
0.006579973820682665 ETH $13.15
Gas Used:
255,385 Gas / 25.764918929 Gwei

Emitted Events:

224 ChungosContract.Approval( owner=0x975cb5aa4e3cf34532490ccd0019b74ad4ab237a, approved=0x00000000...000000000, tokenId=828 )
225 ChungosContract.Transfer( from=0x975cb5aa4e3cf34532490ccd0019b74ad4ab237a, to=[Sender] 0x44500a3d9b73b605ae860d0c12d7cbf31a2da5e5, tokenId=828 )
226 0x000000000000ad05ccc4f10045630fb830b95127.0x61cbb2a3dee0b6064c2e681aadd61677fb4ef319f0b547508d495626f5a62f64( 0x61cbb2a3dee0b6064c2e681aadd61677fb4ef319f0b547508d495626f5a62f64, 0x000000000000000000000000975cb5aa4e3cf34532490ccd0019b74ad4ab237a, 0x00000000000000000000000044500a3d9b73b605ae860d0c12d7cbf31a2da5e5, 0000000000000000000000000000000000000000000000000000000000000080, 0ed2563beaa4d7b08d9734544c901c74b77a7b6ba04adb57f6c2438bd11735c4, 00000000000000000000000000000000000000000000000000000000000002c0, 3a127c69a42bfd7eca9787254b6554c7baa1a35dd6cc581b91c4e0ea4160b33f, 000000000000000000000000975cb5aa4e3cf34532490ccd0019b74ad4ab237a, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000dab4a563819e8fd93dba3b25bc3495, 0000000000000000000000008cae61967466ebbf15c12dc802b29594bc04efc6, 000000000000000000000000000000000000000000000000000000000000033c, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000012dfb0cb5e88000, 0000000000000000000000000000000000000000000000000000000063f2c957, 0000000000000000000000000000000000000000000000000000000063f41ad5, 00000000000000000000000000000000000000000000000000000000000001a0, 00000000000000000000000000000000fc91476c1fd564a3f5e097503d98f394, 0000000000000000000000000000000000000000000000000000000000000200, 0000000000000000000000000000000000000000000000000000000000000001, 00000000000000000000000000000000000000000000000000000000000001f4, 00000000000000000000000021c87de6ab8c127b494349cd2de13e4f87424cdd, 0000000000000000000000000000000000000000000000000000000000000001, 0100000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000044500a3d9b73b605ae860d0c12d7cbf31a2da5e5, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000dab4a563819e8fd93dba3b25bc3495, 0000000000000000000000008cae61967466ebbf15c12dc802b29594bc04efc6, 000000000000000000000000000000000000000000000000000000000000033c, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000012dfb0cb5e88000, 0000000000000000000000000000000000000000000000000000000063f2c958, 0000000000000000000000000000000000000000000000000000000063f2d77d, 00000000000000000000000000000000000000000000000000000000000001a0, 00000000000000000000000000000000dd947023f9916a8445ed5505de224072, 00000000000000000000000000000000000000000000000000000000000001c0, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000001, 0100000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...830B95127
(Blur.io: Marketplace)
0x21c87dE6...f87424CdD
(Chungos: Deployer)
41.819904777072518231 Eth41.824154777072518231 Eth0.00425
0x44500A3D...31a2Da5E5
0.160325848553459094 Eth
Nonce: 1560
0.068745874732776429 Eth
Nonce: 1561
0.091579973820682665
(Manta-builder)
150.216347587756876656 Eth150.216730665256876656 Eth0.0003830775
0x8CAe6196...4bc04eFc6
0x975CB5Aa...ad4ab237A 0.053184296378216886 Eth0.133934296378216886 Eth0.08075

Execution Trace

ETH 0.085 Blur.io: Marketplace.9a1fc3a7( )
  • ETH 0.085 BlurExchange.execute( sell=[{name:order, type:tuple, order:1, indexed:false, value:[{name:trader, type:address, order:1, indexed:false, value:0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A, valueString:0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A}, {name:side, type:uint8, order:2, indexed:false, value:1, valueString:1}, {name:matchingPolicy, type:address, order:3, indexed:false, value:0x0000000000daB4A563819e8fd93dbA3b25BC3495, valueString:0x0000000000daB4A563819e8fd93dbA3b25BC3495}, {name:collection, type:address, order:4, indexed:false, value:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, valueString:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6}, {name:tokenId, type:uint256, order:5, indexed:false, value:828, valueString:828}, {name:amount, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:paymentToken, type:address, order:7, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:price, type:uint256, order:8, indexed:false, value:85000000000000000, valueString:85000000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1676855639, valueString:1676855639}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1676942037, valueString:1676942037}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:335719786599861772552269372944792744852, valueString:335719786599861772552269372944792744852}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}], valueString:[{name:trader, type:address, order:1, indexed:false, value:0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A, valueString:0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A}, {name:side, type:uint8, order:2, indexed:false, value:1, valueString:1}, {name:matchingPolicy, type:address, order:3, indexed:false, value:0x0000000000daB4A563819e8fd93dbA3b25BC3495, valueString:0x0000000000daB4A563819e8fd93dbA3b25BC3495}, {name:collection, type:address, order:4, indexed:false, value:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, valueString:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6}, {name:tokenId, type:uint256, order:5, indexed:false, value:828, valueString:828}, {name:amount, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:paymentToken, type:address, order:7, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:price, type:uint256, order:8, indexed:false, value:85000000000000000, valueString:85000000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1676855639, valueString:1676855639}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1676942037, valueString:1676942037}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:335719786599861772552269372944792744852, valueString:335719786599861772552269372944792744852}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}]}, {name:v, type:uint8, order:2, indexed:false, value:27, valueString:27}, {name:r, type:bytes32, order:3, indexed:false, value:6246416B046001087C2393EAB8996F73A8B7F98207B673B17DE27E4AB206B77A, valueString:6246416B046001087C2393EAB8996F73A8B7F98207B673B17DE27E4AB206B77A}, {name:s, type:bytes32, order:4, indexed:false, value:5C93A7AC5034A223D0F39EC7CCDFEEEDAD39C959E938626DC35C93E0C8BBB3C1, valueString:5C93A7AC5034A223D0F39EC7CCDFEEEDAD39C959E938626DC35C93E0C8BBB3C1}, {name:extraSignature, type:bytes, order:5, indexed:false, value:0x000000000000000000000000000000000000000000000000000000000000001C362E5B7637FF74DB0B75D72B769A6D7A17A5CD184508792DD3B2AB9C6F88DCD87C82494A6295C631A9EB2CEB7BC34B5AEC7C6C01DE0E09B750735F7C655746E1, valueString:0x000000000000000000000000000000000000000000000000000000000000001C362E5B7637FF74DB0B75D72B769A6D7A17A5CD184508792DD3B2AB9C6F88DCD87C82494A6295C631A9EB2CEB7BC34B5AEC7C6C01DE0E09B750735F7C655746E1}, {name:signatureVersion, type:uint8, order:6, indexed:false, value:0, valueString:0}, {name:blockNumber, type:uint256, order:7, indexed:false, value:16666423, valueString:16666423}], buy=[{name:order, type:tuple, order:1, indexed:false, value:[{name:trader, type:address, order:1, indexed:false, value:0x44500A3D9B73B605aE860D0C12d7cbF31a2Da5E5, valueString:0x44500A3D9B73B605aE860D0C12d7cbF31a2Da5E5}, {name:side, type:uint8, order:2, indexed:false, value:0, valueString:0}, {name:matchingPolicy, type:address, order:3, indexed:false, value:0x0000000000daB4A563819e8fd93dbA3b25BC3495, valueString:0x0000000000daB4A563819e8fd93dbA3b25BC3495}, {name:collection, type:address, order:4, indexed:false, value:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, valueString:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6}, {name:tokenId, type:uint256, order:5, indexed:false, value:828, valueString:828}, {name:amount, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:paymentToken, type:address, order:7, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:price, type:uint256, order:8, indexed:false, value:85000000000000000, valueString:85000000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1676855640, valueString:1676855640}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1676859261, valueString:1676859261}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:294530121483628424087318393941506801778, valueString:294530121483628424087318393941506801778}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}], valueString:[{name:trader, type:address, order:1, indexed:false, value:0x44500A3D9B73B605aE860D0C12d7cbF31a2Da5E5, valueString:0x44500A3D9B73B605aE860D0C12d7cbF31a2Da5E5}, {name:side, type:uint8, order:2, indexed:false, value:0, valueString:0}, {name:matchingPolicy, type:address, order:3, indexed:false, value:0x0000000000daB4A563819e8fd93dbA3b25BC3495, valueString:0x0000000000daB4A563819e8fd93dbA3b25BC3495}, {name:collection, type:address, order:4, indexed:false, value:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, valueString:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6}, {name:tokenId, type:uint256, order:5, indexed:false, value:828, valueString:828}, {name:amount, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:paymentToken, type:address, order:7, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:price, type:uint256, order:8, indexed:false, value:85000000000000000, valueString:85000000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1676855640, valueString:1676855640}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1676859261, valueString:1676859261}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:294530121483628424087318393941506801778, valueString:294530121483628424087318393941506801778}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}]}, {name:v, type:uint8, order:2, indexed:false, value:0, valueString:0}, {name:r, type:bytes32, order:3, indexed:false, value:0000000000000000000000000000000000000000000000000000000000000000, valueString:0000000000000000000000000000000000000000000000000000000000000000}, {name:s, type:bytes32, order:4, indexed:false, value:0000000000000000000000000000000000000000000000000000000000000000, valueString:0000000000000000000000000000000000000000000000000000000000000000}, {name:extraSignature, type:bytes, order:5, indexed:false, value:0x000000000000000000000000000000000000000000000000000000000000001B7259CA771C925029462FB9A250D715F59694704F8723BC5F057519BCC6F79D2744B1061A9E595E13FD7C9259EB401BD843F76A2DFEA23B4BA821895BCDC176A6, valueString:0x000000000000000000000000000000000000000000000000000000000000001B7259CA771C925029462FB9A250D715F59694704F8723BC5F057519BCC6F79D2744B1061A9E595E13FD7C9259EB401BD843F76A2DFEA23B4BA821895BCDC176A6}, {name:signatureVersion, type:uint8, order:6, indexed:false, value:0, valueString:0}, {name:blockNumber, type:uint256, order:7, indexed:false, value:16666423, valueString:16666423}] )
    • Null: 0x000...001.9a56f679( )
    • Null: 0x000...001.4a2a473b( )
    • Null: 0x000...001.9eadd919( )
    • 0x3a35a3102b5c6bd1e4d3237248be071ef53c8331.874516cd( )
    • StandardPolicyERC721.canMatchMakerAsk( makerAsk=[{name:trader, type:address, order:1, indexed:false, value:0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A, valueString:0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A}, {name:side, type:uint8, order:2, indexed:false, value:1, valueString:1}, {name:matchingPolicy, type:address, order:3, indexed:false, value:0x0000000000daB4A563819e8fd93dbA3b25BC3495, valueString:0x0000000000daB4A563819e8fd93dbA3b25BC3495}, {name:collection, type:address, order:4, indexed:false, value:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, valueString:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6}, {name:tokenId, type:uint256, order:5, indexed:false, value:828, valueString:828}, {name:amount, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:paymentToken, type:address, order:7, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:price, type:uint256, order:8, indexed:false, value:85000000000000000, valueString:85000000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1676855639, valueString:1676855639}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1676942037, valueString:1676942037}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:335719786599861772552269372944792744852, valueString:335719786599861772552269372944792744852}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}], takerBid=[{name:trader, type:address, order:1, indexed:false, value:0x44500A3D9B73B605aE860D0C12d7cbF31a2Da5E5, valueString:0x44500A3D9B73B605aE860D0C12d7cbF31a2Da5E5}, {name:side, type:uint8, order:2, indexed:false, value:0, valueString:0}, {name:matchingPolicy, type:address, order:3, indexed:false, value:0x0000000000daB4A563819e8fd93dbA3b25BC3495, valueString:0x0000000000daB4A563819e8fd93dbA3b25BC3495}, {name:collection, type:address, order:4, indexed:false, value:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, valueString:0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6}, {name:tokenId, type:uint256, order:5, indexed:false, value:828, valueString:828}, {name:amount, type:uint256, order:6, indexed:false, value:1, valueString:1}, {name:paymentToken, type:address, order:7, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:price, type:uint256, order:8, indexed:false, value:85000000000000000, valueString:85000000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1676855640, valueString:1676855640}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1676859261, valueString:1676859261}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:294530121483628424087318393941506801778, valueString:294530121483628424087318393941506801778}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}] ) => ( True, 85000000000000000, 828, 1, 0 )
    • ETH 0.00425 Chungos: Deployer.CALL( )
    • ETH 0.08075 0x975cb5aa4e3cf34532490ccd0019b74ad4ab237a.CALL( )
    • Blur: Execution Delegate.789f93f6( )
      • ChungosContract.safeTransferFrom( from=0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A, to=0x44500A3D9B73B605aE860D0C12d7cbF31a2Da5E5, tokenId=828 )
        • OperatorFilterRegistry.isOperatorAllowed( registrant=0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, operator=0x00000000000111AbE46ff893f3B2fdF1F759a8A8 ) => ( True )
        • OperatorFilterRegistry.isOperatorAllowed( registrant=0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, operator=0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A ) => ( True )
        • OperatorFilterRegistry.isOperatorAllowed( registrant=0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, operator=0x00000000000111AbE46ff893f3B2fdF1F759a8A8 ) => ( True )
        • OperatorFilterRegistry.isOperatorAllowed( registrant=0x8CAe61967466eBBf15c12Dc802b29594bc04eFc6, operator=0x975CB5Aa4e3CF34532490cCD0019b74ad4ab237A ) => ( True )
          File 1 of 4: ChungosContract
          //*********************************************************************//
          //*********************************************************************//
          //
          // CHUNGO CHUNGO CHUNGO
          //
          //*********************************************************************//
          //*********************************************************************//
            
          //-------------DEPENDENCIES--------------------------//
          
          // File: @openzeppelin/contracts/utils/Address.sol
          
          
          // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
          
          pragma solidity ^0.8.1;
          
          /**
           * @dev Collection of functions related to the address type
           */
          library Address {
              /**
               * @dev Returns true if account is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, isContract will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               *
               * [IMPORTANT]
               * ====
               * You shouldn't rely on isContract to protect against flash loan attacks!
               *
               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
               * constructor.
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize/address.code.length, which returns 0
                  // for contracts in construction, since the code is only stored at the end
                  // of the constructor execution.
          
                  return account.code.length > 0;
              }
          
              /**
               * @dev Replacement for Solidity's transfer: sends amount wei to
               * recipient, forwarding all available gas and reverting on errors.
               *
               * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
               * of certain opcodes, possibly making contracts go over the 2300 gas limit
               * imposed by transfer, making them unable to receive funds via
               * transfer. {sendValue} removes this limitation.
               *
               * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
               *
               * IMPORTANT: because control is transferred to recipient, care must be
               * taken to not create reentrancy vulnerabilities. Consider using
               * {ReentrancyGuard} or the
               * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
               */
              function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, "Address: insufficient balance");
          
                  (bool success, ) = recipient.call{value: amount}("");
                  require(success, "Address: unable to send value, recipient may have reverted");
              }
          
              /**
               * @dev Performs a Solidity function call using a low level call. A
               * plain call is an unsafe replacement for a function call: use this
               * function instead.
               *
               * If target reverts with a revert reason, it is bubbled up by this
               * function (like regular Solidity function calls).
               *
               * Returns the raw returned data. To convert to the expected return value,
               * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[abi.decode].
               *
               * Requirements:
               *
               * - target must be a contract.
               * - calling target with data must not revert.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCall(target, data, "Address: low-level call failed");
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[functionCall], but with
               * errorMessage as a fallback revert reason when target reverts.
               *
               * _Available since v3.1._
               */
              function functionCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0, errorMessage);
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[functionCall],
               * but also transferring value wei to target.
               *
               * Requirements:
               *
               * - the calling contract must have an ETH balance of at least value.
               * - the called Solidity function must be payable.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(
                  address target,
                  bytes memory data,
                  uint256 value
              ) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
              }
          
              /**
               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[functionCallWithValue], but
               * with errorMessage as a fallback revert reason when target reverts.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(
                  address target,
                  bytes memory data,
                  uint256 value,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  require(address(this).balance >= value, "Address: insufficient balance for call");
                  require(isContract(target), "Address: call to non-contract");
          
                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                  return verifyCallResult(success, returndata, errorMessage);
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[functionCall],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                  return functionStaticCall(target, data, "Address: low-level static call failed");
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[functionCall],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal view returns (bytes memory) {
                  require(isContract(target), "Address: static call to non-contract");
          
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return verifyCallResult(success, returndata, errorMessage);
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[functionCall],
               * but performing a delegate call.
               *
               * _Available since v3.4._
               */
              function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionDelegateCall(target, data, "Address: low-level delegate call failed");
              }
          
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[functionCall],
               * but performing a delegate call.
               *
               * _Available since v3.4._
               */
              function functionDelegateCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  require(isContract(target), "Address: delegate call to non-contract");
          
                  (bool success, bytes memory returndata) = target.delegatecall(data);
                  return verifyCallResult(success, returndata, errorMessage);
              }
          
              /**
               * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
               * revert reason using the provided one.
               *
               * _Available since v4.3._
               */
              function verifyCallResult(
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal pure returns (bytes memory) {
                  if (success) {
                      return returndata;
                  } else {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
          
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
          }
          
          // File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @title ERC721 token receiver interface
           * @dev Interface for any contract that wants to support safeTransfers
           * from ERC721 asset contracts.
           */
          interface IERC721Receiver {
              /**
               * @dev Whenever an {IERC721} tokenId token is transferred to this contract via {IERC721-safeTransferFrom}
               * by operator from from, this function is called.
               *
               * It must return its Solidity selector to confirm the token transfer.
               * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
               *
               * The selector can be obtained in Solidity with IERC721.onERC721Received.selector.
               */
              function onERC721Received(
                  address operator,
                  address from,
                  uint256 tokenId,
                  bytes calldata data
              ) external returns (bytes4);
          }
          
          // File: @openzeppelin/contracts/utils/introspection/IERC165.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165 {
              /**
               * @dev Returns true if this contract implements the interface defined by
               * interfaceId. See the corresponding
               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          
          // File: @openzeppelin/contracts/utils/introspection/ERC165.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * 
           *
           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
           */
          abstract contract ERC165 is IERC165 {
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IERC165).interfaceId;
              }
          }
          
          // File: @openzeppelin/contracts/token/ERC721/IERC721.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @dev Required interface of an ERC721 compliant contract.
           */
          interface IERC721 is IERC165 {
              /**
               * @dev Emitted when tokenId token is transferred from from to to.
               */
              event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          
              /**
               * @dev Emitted when owner enables approved to manage the tokenId token.
               */
              event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          
              /**
               * @dev Emitted when owner enables or disables (approved) operator to manage all of its assets.
               */
              event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          
              /**
               * @dev Returns the number of tokens in owner's account.
               */
              function balanceOf(address owner) external view returns (uint256 balance);
          
              /**
               * @dev Returns the owner of the tokenId token.
               *
               * Requirements:
               *
               * - tokenId must exist.
               */
              function ownerOf(uint256 tokenId) external view returns (address owner);
          
              /**
               * @dev Safely transfers tokenId token from from to to, checking first that contract recipients
               * are aware of the ERC721 protocol to prevent tokens from being forever locked.
               *
               * Requirements:
               *
               * - from cannot be the zero address.
               * - to cannot be the zero address.
               * - tokenId token must exist and be owned by from.
               * - If the caller is not from, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
               * - If to refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
               *
               * Emits a {Transfer} event.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) external;
          
              /**
               * @dev Transfers tokenId token from from to to.
               *
               * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
               *
               * Requirements:
               *
               * - from cannot be the zero address.
               * - to cannot be the zero address.
               * - tokenId token must be owned by from.
               * - If the caller is not from, it must be approved to move this token by either {approve} or {setApprovalForAll}.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) external;
          
              /**
               * @dev Gives permission to to to transfer tokenId token to another account.
               * The approval is cleared when the token is transferred.
               *
               * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
               *
               * Requirements:
               *
               * - The caller must own the token or be an approved operator.
               * - tokenId must exist.
               *
               * Emits an {Approval} event.
               */
              function approve(address to, uint256 tokenId) external;
          
              /**
               * @dev Returns the account approved for tokenId token.
               *
               * Requirements:
               *
               * - tokenId must exist.
               */
              function getApproved(uint256 tokenId) external view returns (address operator);
          
              /**
               * @dev Approve or remove operator as an operator for the caller.
               * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
               *
               * Requirements:
               *
               * - The operator cannot be the caller.
               *
               * Emits an {ApprovalForAll} event.
               */
              function setApprovalForAll(address operator, bool _approved) external;
          
              /**
               * @dev Returns if the operator is allowed to manage all of the assets of owner.
               *
               * See {setApprovalForAll}
               */
              function isApprovedForAll(address owner, address operator) external view returns (bool);
          
              /**
               * @dev Safely transfers tokenId token from from to to.
               *
               * Requirements:
               *
               * - from cannot be the zero address.
               * - to cannot be the zero address.
               * - tokenId token must exist and be owned by from.
               * - If the caller is not from, it must be approved to move this token by either {approve} or {setApprovalForAll}.
               * - If to refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
               *
               * Emits a {Transfer} event.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes calldata data
              ) external;
          }
          
          // File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol
          
          
          // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
           * @dev See https://eips.ethereum.org/EIPS/eip-721
           */
          interface IERC721Enumerable is IERC721 {
              /**
               * @dev Returns the total amount of tokens stored by the contract.
               */
              function totalSupply() external view returns (uint256);
          
              /**
               * @dev Returns a token ID owned by owner at a given index of its token list.
               * Use along with {balanceOf} to enumerate all of owner's tokens.
               */
              function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
          
              /**
               * @dev Returns a token ID at a given index of all the tokens stored by the contract.
               * Use along with {totalSupply} to enumerate all tokens.
               */
              function tokenByIndex(uint256 index) external view returns (uint256);
          }
          
          // File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
           * @dev See https://eips.ethereum.org/EIPS/eip-721
           */
          interface IERC721Metadata is IERC721 {
              /**
               * @dev Returns the token collection name.
               */
              function name() external view returns (string memory);
          
              /**
               * @dev Returns the token collection symbol.
               */
              function symbol() external view returns (string memory);
          
              /**
               * @dev Returns the Uniform Resource Identifier (URI) for tokenId token.
               */
              function tokenURI(uint256 tokenId) external view returns (string memory);
          }
          
          // File: @openzeppelin/contracts/utils/Strings.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev String operations.
           */
          library Strings {
              bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
          
              /**
               * @dev Converts a uint256 to its ASCII string decimal representation.
               */
              function toString(uint256 value) internal pure returns (string memory) {
                  // Inspired by OraclizeAPI's implementation - MIT licence
                  // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
          
                  if (value == 0) {
                      return "0";
                  }
                  uint256 temp = value;
                  uint256 digits;
                  while (temp != 0) {
                      digits++;
                      temp /= 10;
                  }
                  bytes memory buffer = new bytes(digits);
                  while (value != 0) {
                      digits -= 1;
                      buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                      value /= 10;
                  }
                  return string(buffer);
              }
          
              /**
               * @dev Converts a uint256 to its ASCII string hexadecimal representation.
               */
              function toHexString(uint256 value) internal pure returns (string memory) {
                  if (value == 0) {
                      return "0x00";
                  }
                  uint256 temp = value;
                  uint256 length = 0;
                  while (temp != 0) {
                      length++;
                      temp >>= 8;
                  }
                  return toHexString(value, length);
              }
          
              /**
               * @dev Converts a uint256 to its ASCII string hexadecimal representation with fixed length.
               */
              function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                  bytes memory buffer = new bytes(2 * length + 2);
                  buffer[0] = "0";
                  buffer[1] = "x";
                  for (uint256 i = 2 * length + 1; i > 1; --i) {
                      buffer[i] = _HEX_SYMBOLS[value & 0xf];
                      value >>= 4;
                  }
                  require(value == 0, "Strings: hex length insufficient");
                  return string(buffer);
              }
          }
          
          // File: @openzeppelin/contracts/security/ReentrancyGuard.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Contract module that helps prevent reentrant calls to a function.
           *
           * Inheriting from ReentrancyGuard will make the {nonReentrant} modifier
           * available, which can be applied to functions to make sure there are no nested
           * (reentrant) calls to them.
           *
           * Note that because there is a single nonReentrant guard, functions marked as
           * nonReentrant may not call one another. This can be worked around by making
           * those functions private, and then adding external nonReentrant entry
           * points to them.
           *
           * TIP: If you would like to learn more about reentrancy and alternative ways
           * to protect against it, check out our blog post
           * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
           */
          abstract contract ReentrancyGuard {
              // Booleans are more expensive than uint256 or any type that takes up a full
              // word because each write operation emits an extra SLOAD to first read the
              // slot's contents, replace the bits taken up by the boolean, and then write
              // back. This is the compiler's defense against contract upgrades and
              // pointer aliasing, and it cannot be disabled.
          
              // The values being non-zero value makes deployment a bit more expensive,
              // but in exchange the refund on every call to nonReentrant will be lower in
              // amount. Since refunds are capped to a percentage of the total
              // transaction's gas, it is best to keep them low in cases like this one, to
              // increase the likelihood of the full refund coming into effect.
              uint256 private constant _NOT_ENTERED = 1;
              uint256 private constant _ENTERED = 2;
          
              uint256 private _status;
          
              constructor() {
                  _status = _NOT_ENTERED;
              }
          
              /**
               * @dev Prevents a contract from calling itself, directly or indirectly.
               * Calling a nonReentrant function from another nonReentrant
               * function is not supported. It is possible to prevent this from happening
               * by making the nonReentrant function external, and making it call a
               * private function that does the actual work.
               */
              modifier nonReentrant() {
                  // On the first call to nonReentrant, _notEntered will be true
                  require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
          
                  // Any calls to nonReentrant after this point will fail
                  _status = _ENTERED;
          
                  _;
          
                  // By storing the original value once again, a refund is triggered (see
                  // https://eips.ethereum.org/EIPS/eip-2200)
                  _status = _NOT_ENTERED;
              }
          }
          
          // File: @openzeppelin/contracts/utils/Context.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          
          pragma solidity ^0.8.0;
          
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
          
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          
          // File: @openzeppelin/contracts/access/Ownable.sol
          
          
          // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
          
          pragma solidity ^0.8.0;
          
          
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * onlyOwner, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract Ownable is Context {
              address private _owner;
          
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              constructor() {
                  _transferOwnership(_msgSender());
              }
          
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
          
              /**
               * @dev Throws if called by any account other than the owner.
               */
              function _onlyOwner() private view {
                 require(owner() == _msgSender(), "Ownable: caller is not the owner");
              }
          
              modifier onlyOwner() {
                  _onlyOwner();
                  _;
              }
          
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * onlyOwner functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
          
              /**
               * @dev Transfers ownership of the contract to a new account (newOwner).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
          
              /**
               * @dev Transfers ownership of the contract to a new account (newOwner).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
          }
          
          // File contracts/OperatorFilter/IOperatorFilterRegistry.sol
          pragma solidity ^0.8.9;
          
          interface IOperatorFilterRegistry {
              function isOperatorAllowed(address registrant, address operator) external view returns (bool);
              function register(address registrant) external;
              function registerAndSubscribe(address registrant, address subscription) external;
              function registerAndCopyEntries(address registrant, address registrantToCopy) external;
              function updateOperator(address registrant, address operator, bool filtered) external;
              function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
              function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
              function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
              function subscribe(address registrant, address registrantToSubscribe) external;
              function unsubscribe(address registrant, bool copyExistingEntries) external;
              function subscriptionOf(address addr) external returns (address registrant);
              function subscribers(address registrant) external returns (address[] memory);
              function subscriberAt(address registrant, uint256 index) external returns (address);
              function copyEntriesOf(address registrant, address registrantToCopy) external;
              function isOperatorFiltered(address registrant, address operator) external returns (bool);
              function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
              function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
              function filteredOperators(address addr) external returns (address[] memory);
              function filteredCodeHashes(address addr) external returns (bytes32[] memory);
              function filteredOperatorAt(address registrant, uint256 index) external returns (address);
              function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
              function isRegistered(address addr) external returns (bool);
              function codeHashOf(address addr) external returns (bytes32);
          }
          
          // File contracts/OperatorFilter/OperatorFilterer.sol
          pragma solidity ^0.8.9;
          
          abstract contract OperatorFilterer {
              error OperatorNotAllowed(address operator);
          
              IOperatorFilterRegistry constant operatorFilterRegistry =
                  IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
          
              constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
                  // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
                  // will not revert, but the contract will need to be registered with the registry once it is deployed in
                  // order for the modifier to filter addresses.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      if (subscribe) {
                          operatorFilterRegistry.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                      } else {
                          if (subscriptionOrRegistrantToCopy != address(0)) {
                              operatorFilterRegistry.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                          } else {
                              operatorFilterRegistry.register(address(this));
                          }
                      }
                  }
              }
          
              function _onlyAllowedOperator(address from) private view {
                if (
                    !(
                        operatorFilterRegistry.isOperatorAllowed(address(this), msg.sender)
                        && operatorFilterRegistry.isOperatorAllowed(address(this), from)
                    )
                ) {
                    revert OperatorNotAllowed(msg.sender);
                }
              }
          
              modifier onlyAllowedOperator(address from) virtual {
                  // Check registry code length to facilitate testing in environments without a deployed registry.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      // Allow spending tokens from addresses with balance
                      // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                      // from an EOA.
                      if (from == msg.sender) {
                          _;
                          return;
                      }
                      _onlyAllowedOperator(from);
                  }
                  _;
              }
          
              modifier onlyAllowedOperatorApproval(address operator) virtual {
                  _checkFilterOperator(operator);
                  _;
              }
          
              function _checkFilterOperator(address operator) internal view virtual {
                  // Check registry code length to facilitate testing in environments without a deployed registry.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      if (!operatorFilterRegistry.isOperatorAllowed(address(this), operator)) {
                          revert OperatorNotAllowed(operator);
                      }
                  }
              }
          }
          
          //-------------END DEPENDENCIES------------------------//
          
          
            
          error TransactionCapExceeded();
          error PublicMintingClosed();
          error ExcessiveOwnedMints();
          error MintZeroQuantity();
          error InvalidPayment();
          error CapExceeded();
          error IsAlreadyUnveiled();
          error ValueCannotBeZero();
          error CannotBeNullAddress();
          error NoStateChange();
          
          error PublicMintClosed();
          error AllowlistMintClosed();
          
          error AddressNotAllowlisted();
          error AllowlistDropTimeHasNotPassed();
          error PublicDropTimeHasNotPassed();
          error DropTimeNotInFuture();
          
          error OnlyERC20MintingEnabled();
          error ERC20TokenNotApproved();
          error ERC20InsufficientBalance();
          error ERC20InsufficientAllowance();
          error ERC20TransferFailed();
          
          error ClaimModeDisabled();
          error IneligibleRedemptionContract();
          error TokenAlreadyRedeemed();
          error InvalidOwnerForRedemption();
          error InvalidApprovalForRedemption();
          
          error ERC721RestrictedApprovalAddressRestricted();
            
            
          // Rampp Contracts v2.1 (Teams.sol)
          
          error InvalidTeamAddress();
          error DuplicateTeamAddress();
          pragma solidity ^0.8.0;
          
          /**
          * Teams is a contract implementation to extend upon Ownable that allows multiple controllers
          * of a single contract to modify specific mint settings but not have overall ownership of the contract.
          * This will easily allow cross-collaboration via Mintplex.xyz.
          **/
          abstract contract Teams is Ownable{
            mapping (address => bool) internal team;
          
            /**
            * @dev Adds an address to the team. Allows them to execute protected functions
            * @param _address the ETH address to add, cannot be 0x and cannot be in team already
            **/
            function addToTeam(address _address) public onlyOwner {
              if(_address == address(0)) revert InvalidTeamAddress();
              if(inTeam(_address)) revert DuplicateTeamAddress();
            
              team[_address] = true;
            }
          
            /**
            * @dev Removes an address to the team.
            * @param _address the ETH address to remove, cannot be 0x and must be in team
            **/
            function removeFromTeam(address _address) public onlyOwner {
              if(_address == address(0)) revert InvalidTeamAddress();
              if(!inTeam(_address)) revert InvalidTeamAddress();
            
              team[_address] = false;
            }
          
            /**
            * @dev Check if an address is valid and active in the team
            * @param _address ETH address to check for truthiness
            **/
            function inTeam(address _address)
              public
              view
              returns (bool)
            {
              if(_address == address(0)) revert InvalidTeamAddress();
              return team[_address] == true;
            }
          
            /**
            * @dev Throws if called by any account other than the owner or team member.
            */
            function _onlyTeamOrOwner() private view {
              bool _isOwner = owner() == _msgSender();
              bool _isTeam = inTeam(_msgSender());
              require(_isOwner || _isTeam, "Team: caller is not the owner or in Team.");
            }
          
            modifier onlyTeamOrOwner() {
              _onlyTeamOrOwner();
              _;
            }
          }
          
          
            
            pragma solidity ^0.8.0;
          
            /**
            * @dev These functions deal with verification of Merkle Trees proofs.
            *
            * The proofs can be generated using the JavaScript library
            * https://github.com/miguelmota/merkletreejs[merkletreejs].
            * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
            *
            *
            * WARNING: You should avoid using leaf values that are 64 bytes long prior to
            * hashing, or use a hash function other than keccak256 for hashing leaves.
            * This is because the concatenation of a sorted pair of internal nodes in
            * the merkle tree could be reinterpreted as a leaf value.
            */
            library MerkleProof {
                /**
                * @dev Returns true if a 'leaf' can be proved to be a part of a Merkle tree
                * defined by 'root'. For this, a 'proof' must be provided, containing
                * sibling hashes on the branch from the leaf to the root of the tree. Each
                * pair of leaves and each pair of pre-images are assumed to be sorted.
                */
                function verify(
                    bytes32[] memory proof,
                    bytes32 root,
                    bytes32 leaf
                ) internal pure returns (bool) {
                    return processProof(proof, leaf) == root;
                }
          
                /**
                * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
                * from 'leaf' using 'proof'. A 'proof' is valid if and only if the rebuilt
                * hash matches the root of the tree. When processing the proof, the pairs
                * of leafs & pre-images are assumed to be sorted.
                *
                * _Available since v4.4._
                */
                function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
                    bytes32 computedHash = leaf;
                    for (uint256 i = 0; i < proof.length; i++) {
                        bytes32 proofElement = proof[i];
                        if (computedHash <= proofElement) {
                            // Hash(current computed hash + current element of the proof)
                            computedHash = _efficientHash(computedHash, proofElement);
                        } else {
                            // Hash(current element of the proof + current computed hash)
                            computedHash = _efficientHash(proofElement, computedHash);
                        }
                    }
                    return computedHash;
                }
          
                function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                    assembly {
                        mstore(0x00, a)
                        mstore(0x20, b)
                        value := keccak256(0x00, 0x40)
                    }
                }
            }
          
          
            // File: Allowlist.sol
          
            pragma solidity ^0.8.0;
          
            abstract contract Allowlist is Teams {
              bytes32 public merkleRoot;
              bool public onlyAllowlistMode = false;
          
              /**
               * @dev Update merkle root to reflect changes in Allowlist
               * @param _newMerkleRoot new merkle root to reflect most recent Allowlist
               */
              function updateMerkleRoot(bytes32 _newMerkleRoot) public onlyTeamOrOwner {
                if(_newMerkleRoot == merkleRoot) revert NoStateChange();
                merkleRoot = _newMerkleRoot;
              }
          
              /**
               * @dev Check the proof of an address if valid for merkle root
               * @param _to address to check for proof
               * @param _merkleProof Proof of the address to validate against root and leaf
               */
              function isAllowlisted(address _to, bytes32[] calldata _merkleProof) public view returns(bool) {
                if(merkleRoot == 0) revert ValueCannotBeZero();
                bytes32 leaf = keccak256(abi.encodePacked(_to));
          
                return MerkleProof.verify(_merkleProof, merkleRoot, leaf);
              }
          
              
              function enableAllowlistOnlyMode() public onlyTeamOrOwner {
                onlyAllowlistMode = true;
              }
          
              function disableAllowlistOnlyMode() public onlyTeamOrOwner {
                  onlyAllowlistMode = false;
              }
            }
            
            
          /**
           * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
           * the Metadata and Enumerable extension. Built to optimize for lower gas during batch mints.
           *
           * Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
           * 
           * Assumes the number of issuable tokens (collection size) is capped and fits in a uint128.
           *
           * Does not support burning tokens to address(0).
           */
          contract ERC721A is
            Context,
            ERC165,
            IERC721,
            IERC721Metadata,
            IERC721Enumerable,
            Teams
            , OperatorFilterer
          {
            using Address for address;
            using Strings for uint256;
          
            struct TokenOwnership {
              address addr;
              uint64 startTimestamp;
            }
          
            struct AddressData {
              uint128 balance;
              uint128 numberMinted;
            }
          
            uint256 private currentIndex;
          
            uint256 public immutable collectionSize;
            uint256 public maxBatchSize;
          
            // Token name
            string private _name;
          
            // Token symbol
            string private _symbol;
          
            // Mapping from token ID to ownership details
            // An empty struct value does not necessarily mean the token is unowned. See ownershipOf implementation for details.
            mapping(uint256 => TokenOwnership) private _ownerships;
          
            // Mapping owner address to address data
            mapping(address => AddressData) private _addressData;
          
            // Mapping from token ID to approved address
            mapping(uint256 => address) private _tokenApprovals;
          
            // Mapping from owner to operator approvals
            mapping(address => mapping(address => bool)) private _operatorApprovals;
          
            /* @dev Mapping of restricted operator approvals set by contract Owner
            * This serves as an optional addition to ERC-721 so
            * that the contract owner can elect to prevent specific addresses/contracts
            * from being marked as the approver for a token. The reason for this
            * is that some projects may want to retain control of where their tokens can/can not be listed
            * either due to ethics, loyalty, or wanting trades to only occur on their personal marketplace.
            * By default, there are no restrictions. The contract owner must deliberatly block an address 
            */
            mapping(address => bool) public restrictedApprovalAddresses;
          
            /**
             * @dev
             * maxBatchSize refers to how much a minter can mint at a time.
             * collectionSize_ refers to how many tokens are in the collection.
             */
            constructor(
              string memory name_,
              string memory symbol_,
              uint256 maxBatchSize_,
              uint256 collectionSize_
            ) OperatorFilterer(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6, true) {
              require(
                collectionSize_ > 0,
                "ERC721A: collection must have a nonzero supply"
              );
              require(maxBatchSize_ > 0, "ERC721A: max batch size must be nonzero");
              _name = name_;
              _symbol = symbol_;
              maxBatchSize = maxBatchSize_;
              collectionSize = collectionSize_;
              currentIndex = _startTokenId();
            }
          
            /**
            * To change the starting tokenId, please override this function.
            */
            function _startTokenId() internal view virtual returns (uint256) {
              return 1;
            }
          
            /**
             * @dev See {IERC721Enumerable-totalSupply}.
             */
            function totalSupply() public view override returns (uint256) {
              return _totalMinted();
            }
          
            function currentTokenId() public view returns (uint256) {
              return _totalMinted();
            }
          
            function getNextTokenId() public view returns (uint256) {
                return _totalMinted() + 1;
            }
          
            /**
            * Returns the total amount of tokens minted in the contract.
            */
            function _totalMinted() internal view returns (uint256) {
              unchecked {
                return currentIndex - _startTokenId();
              }
            }
          
            /**
             * @dev See {IERC721Enumerable-tokenByIndex}.
             */
            function tokenByIndex(uint256 index) public view override returns (uint256) {
              require(index < totalSupply(), "ERC721A: global index out of bounds");
              return index;
            }
          
            /**
             * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
             * This read function is O(collectionSize). If calling from a separate contract, be sure to test gas first.
             * It may also degrade with extremely large collection sizes (e.g >> 10000), test for your use case.
             */
            function tokenOfOwnerByIndex(address owner, uint256 index)
              public
              view
              override
              returns (uint256)
            {
              require(index < balanceOf(owner), "ERC721A: owner index out of bounds");
              uint256 numMintedSoFar = totalSupply();
              uint256 tokenIdsIdx = 0;
              address currOwnershipAddr = address(0);
              for (uint256 i = 0; i < numMintedSoFar; i++) {
                TokenOwnership memory ownership = _ownerships[i];
                if (ownership.addr != address(0)) {
                  currOwnershipAddr = ownership.addr;
                }
                if (currOwnershipAddr == owner) {
                  if (tokenIdsIdx == index) {
                    return i;
                  }
                  tokenIdsIdx++;
                }
              }
              revert("ERC721A: unable to get token of owner by index");
            }
          
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId)
              public
              view
              virtual
              override(ERC165, IERC165)
              returns (bool)
            {
              return
                interfaceId == type(IERC721).interfaceId ||
                interfaceId == type(IERC721Metadata).interfaceId ||
                interfaceId == type(IERC721Enumerable).interfaceId ||
                super.supportsInterface(interfaceId);
            }
          
            /**
             * @dev See {IERC721-balanceOf}.
             */
            function balanceOf(address owner) public view override returns (uint256) {
              require(owner != address(0), "ERC721A: balance query for the zero address");
              return uint256(_addressData[owner].balance);
            }
          
            function _numberMinted(address owner) internal view returns (uint256) {
              require(
                owner != address(0),
                "ERC721A: number minted query for the zero address"
              );
              return uint256(_addressData[owner].numberMinted);
            }
          
            function ownershipOf(uint256 tokenId)
              internal
              view
              returns (TokenOwnership memory)
            {
              uint256 curr = tokenId;
          
              unchecked {
                  if (_startTokenId() <= curr && curr < currentIndex) {
                      TokenOwnership memory ownership = _ownerships[curr];
                      if (ownership.addr != address(0)) {
                          return ownership;
                      }
          
                      // Invariant:
                      // There will always be an ownership that has an address and is not burned
                      // before an ownership that does not have an address and is not burned.
                      // Hence, curr will not underflow.
                      while (true) {
                          curr--;
                          ownership = _ownerships[curr];
                          if (ownership.addr != address(0)) {
                              return ownership;
                          }
                      }
                  }
              }
          
              revert("ERC721A: unable to determine the owner of token");
            }
          
            /**
             * @dev See {IERC721-ownerOf}.
             */
            function ownerOf(uint256 tokenId) public view override returns (address) {
              return ownershipOf(tokenId).addr;
            }
          
            /**
             * @dev See {IERC721Metadata-name}.
             */
            function name() public view virtual override returns (string memory) {
              return _name;
            }
          
            /**
             * @dev See {IERC721Metadata-symbol}.
             */
            function symbol() public view virtual override returns (string memory) {
              return _symbol;
            }
          
            /**
             * @dev See {IERC721Metadata-tokenURI}.
             */
            function tokenURI(uint256 tokenId)
              public
              view
              virtual
              override
              returns (string memory)
            {
              string memory baseURI = _baseURI();
              string memory extension = _baseURIExtension();
              return
                bytes(baseURI).length > 0
                  ? string(abi.encodePacked(baseURI, tokenId.toString(), extension))
                  : "";
            }
          
            /**
             * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
             * token will be the concatenation of the baseURI and the tokenId. Empty
             * by default, can be overriden in child contracts.
             */
            function _baseURI() internal view virtual returns (string memory) {
              return "";
            }
          
            /**
             * @dev Base URI extension used for computing {tokenURI}. If set, the resulting URI for each
             * token will be the concatenation of the baseURI, tokenId, and this value. Empty
             * by default, can be overriden in child contracts.
             */
            function _baseURIExtension() internal view virtual returns (string memory) {
              return "";
            }
          
            /**
             * @dev Sets the value for an address to be in the restricted approval address pool.
             * Setting an address to true will disable token owners from being able to mark the address
             * for approval for trading. This would be used in theory to prevent token owners from listing
             * on specific marketplaces or protcols. Only modifible by the contract owner/team.
             * @param _address the marketplace/user to modify restriction status of
             * @param _isRestricted restriction status of the _address to be set. true => Restricted, false => Open
             */
            function setApprovalRestriction(address _address, bool _isRestricted) public onlyTeamOrOwner {
              restrictedApprovalAddresses[_address] = _isRestricted;
            }
          
            /**
             * @dev See {IERC721-approve}.
             */
            function approve(address to, uint256 tokenId) public override onlyAllowedOperatorApproval(to) {
              address owner = ERC721A.ownerOf(tokenId);
              require(to != owner, "ERC721A: approval to current owner");
              if(restrictedApprovalAddresses[to]) revert ERC721RestrictedApprovalAddressRestricted();
          
              require(
                _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                "ERC721A: approve caller is not owner nor approved for all"
              );
          
              _approve(to, tokenId, owner);
            }
          
            /**
             * @dev See {IERC721-getApproved}.
             */
            function getApproved(uint256 tokenId) public view override returns (address) {
              require(_exists(tokenId), "ERC721A: approved query for nonexistent token");
          
              return _tokenApprovals[tokenId];
            }
          
            /**
             * @dev See {IERC721-setApprovalForAll}.
             */
            function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
              require(operator != _msgSender(), "ERC721A: approve to caller");
              if(restrictedApprovalAddresses[operator]) revert ERC721RestrictedApprovalAddressRestricted();
          
              _operatorApprovals[_msgSender()][operator] = approved;
              emit ApprovalForAll(_msgSender(), operator, approved);
            }
          
            /**
             * @dev See {IERC721-isApprovedForAll}.
             */
            function isApprovedForAll(address owner, address operator)
              public
              view
              virtual
              override
              returns (bool)
            {
              return _operatorApprovals[owner][operator];
            }
          
            /**
             * @dev See {IERC721-transferFrom}.
             */
            function transferFrom(
              address from,
              address to,
              uint256 tokenId
            ) public override onlyAllowedOperator(from) {
              _transfer(from, to, tokenId);
            }
          
            /**
             * @dev See {IERC721-safeTransferFrom}.
             */
            function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
            ) public override onlyAllowedOperator(from) {
              safeTransferFrom(from, to, tokenId, "");
            }
          
            /**
             * @dev See {IERC721-safeTransferFrom}.
             */
            function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes memory _data
            ) public override onlyAllowedOperator(from) {
              _transfer(from, to, tokenId);
              require(
                _checkOnERC721Received(from, to, tokenId, _data),
                "ERC721A: transfer to non ERC721Receiver implementer"
              );
            }
          
            /**
             * @dev Returns whether tokenId exists.
             *
             * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
             *
             * Tokens start existing when they are minted (_mint),
             */
            function _exists(uint256 tokenId) internal view returns (bool) {
              return _startTokenId() <= tokenId && tokenId < currentIndex;
            }
          
            function _safeMint(address to, uint256 quantity, bool isAdminMint) internal {
              _safeMint(to, quantity, isAdminMint, "");
            }
          
            /**
             * @dev Mints quantity tokens and transfers them to to.
             *
             * Requirements:
             *
             * - there must be quantity tokens remaining unminted in the total collection.
             * - to cannot be the zero address.
             * - quantity cannot be larger than the max batch size.
             *
             * Emits a {Transfer} event.
             */
            function _safeMint(
              address to,
              uint256 quantity,
              bool isAdminMint,
              bytes memory _data
            ) internal {
              uint256 startTokenId = currentIndex;
              require(to != address(0), "ERC721A: mint to the zero address");
              // We know if the first token in the batch doesn't exist, the other ones don't as well, because of serial ordering.
              require(!_exists(startTokenId), "ERC721A: token already minted");
          
              // For admin mints we do not want to enforce the maxBatchSize limit
              if (isAdminMint == false) {
                  require(quantity <= maxBatchSize, "ERC721A: quantity to mint too high");
              }
          
              _beforeTokenTransfers(address(0), to, startTokenId, quantity);
          
              AddressData memory addressData = _addressData[to];
              _addressData[to] = AddressData(
                addressData.balance + uint128(quantity),
                addressData.numberMinted + (isAdminMint ? 0 : uint128(quantity))
              );
              _ownerships[startTokenId] = TokenOwnership(to, uint64(block.timestamp));
          
              uint256 updatedIndex = startTokenId;
          
              for (uint256 i = 0; i < quantity; i++) {
                emit Transfer(address(0), to, updatedIndex);
                require(
                  _checkOnERC721Received(address(0), to, updatedIndex, _data),
                  "ERC721A: transfer to non ERC721Receiver implementer"
                );
                updatedIndex++;
              }
          
              currentIndex = updatedIndex;
              _afterTokenTransfers(address(0), to, startTokenId, quantity);
            }
          
            /**
             * @dev Transfers tokenId from from to to.
             *
             * Requirements:
             *
             * - to cannot be the zero address.
             * - tokenId token must be owned by from.
             *
             * Emits a {Transfer} event.
             */
            function _transfer(
              address from,
              address to,
              uint256 tokenId
            ) private {
              TokenOwnership memory prevOwnership = ownershipOf(tokenId);
          
              bool isApprovedOrOwner = (_msgSender() == prevOwnership.addr ||
                getApproved(tokenId) == _msgSender() ||
                isApprovedForAll(prevOwnership.addr, _msgSender()));
          
              require(
                isApprovedOrOwner,
                "ERC721A: transfer caller is not owner nor approved"
              );
          
              require(
                prevOwnership.addr == from,
                "ERC721A: transfer from incorrect owner"
              );
              require(to != address(0), "ERC721A: transfer to the zero address");
          
              _beforeTokenTransfers(from, to, tokenId, 1);
          
              // Clear approvals from the previous owner
              _approve(address(0), tokenId, prevOwnership.addr);
          
              _addressData[from].balance -= 1;
              _addressData[to].balance += 1;
              _ownerships[tokenId] = TokenOwnership(to, uint64(block.timestamp));
          
              // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
              // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
              uint256 nextTokenId = tokenId + 1;
              if (_ownerships[nextTokenId].addr == address(0)) {
                if (_exists(nextTokenId)) {
                  _ownerships[nextTokenId] = TokenOwnership(
                    prevOwnership.addr,
                    prevOwnership.startTimestamp
                  );
                }
              }
          
              emit Transfer(from, to, tokenId);
              _afterTokenTransfers(from, to, tokenId, 1);
            }
          
            /**
             * @dev Approve to to operate on tokenId
             *
             * Emits a {Approval} event.
             */
            function _approve(
              address to,
              uint256 tokenId,
              address owner
            ) private {
              _tokenApprovals[tokenId] = to;
              emit Approval(owner, to, tokenId);
            }
          
            uint256 public nextOwnerToExplicitlySet = 0;
          
            /**
             * @dev Explicitly set owners to eliminate loops in future calls of ownerOf().
             */
            function _setOwnersExplicit(uint256 quantity) internal {
              uint256 oldNextOwnerToSet = nextOwnerToExplicitlySet;
              require(quantity > 0, "quantity must be nonzero");
              if (currentIndex == _startTokenId()) revert('No Tokens Minted Yet');
          
              uint256 endIndex = oldNextOwnerToSet + quantity - 1;
              if (endIndex > collectionSize - 1) {
                endIndex = collectionSize - 1;
              }
              // We know if the last one in the group exists, all in the group exist, due to serial ordering.
              require(_exists(endIndex), "not enough minted yet for this cleanup");
              for (uint256 i = oldNextOwnerToSet; i <= endIndex; i++) {
                if (_ownerships[i].addr == address(0)) {
                  TokenOwnership memory ownership = ownershipOf(i);
                  _ownerships[i] = TokenOwnership(
                    ownership.addr,
                    ownership.startTimestamp
                  );
                }
              }
              nextOwnerToExplicitlySet = endIndex + 1;
            }
          
            /**
             * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
             * The call is not executed if the target address is not a contract.
             *
             * @param from address representing the previous owner of the given token ID
             * @param to target address that will receive the tokens
             * @param tokenId uint256 ID of the token to be transferred
             * @param _data bytes optional data to send along with the call
             * @return bool whether the call correctly returned the expected magic value
             */
            function _checkOnERC721Received(
              address from,
              address to,
              uint256 tokenId,
              bytes memory _data
            ) private returns (bool) {
              if (to.isContract()) {
                try
                  IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data)
                returns (bytes4 retval) {
                  return retval == IERC721Receiver(to).onERC721Received.selector;
                } catch (bytes memory reason) {
                  if (reason.length == 0) {
                    revert("ERC721A: transfer to non ERC721Receiver implementer");
                  } else {
                    assembly {
                      revert(add(32, reason), mload(reason))
                    }
                  }
                }
              } else {
                return true;
              }
            }
          
            /**
             * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
             *
             * startTokenId - the first token id to be transferred
             * quantity - the amount to be transferred
             *
             * Calling conditions:
             *
             * - When from and to are both non-zero, from's tokenId will be
             * transferred to to.
             * - When from is zero, tokenId will be minted for to.
             */
            function _beforeTokenTransfers(
              address from,
              address to,
              uint256 startTokenId,
              uint256 quantity
            ) internal virtual {}
          
            /**
             * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
             * minting.
             *
             * startTokenId - the first token id to be transferred
             * quantity - the amount to be transferred
             *
             * Calling conditions:
             *
             * - when from and to are both non-zero.
             * - from and to are never both zero.
             */
            function _afterTokenTransfers(
              address from,
              address to,
              uint256 startTokenId,
              uint256 quantity
            ) internal virtual {}
          }
          
          // @title An implementation of ERC-721A with additonal context for 1:1 redemption with another ERC-721
          // @author Mintplex.xyz (Mintplex Labs Inc) (Twitter: @MintplexNFT)
          // @notice -- See Medium article --
          // @custom:experimental This is an experimental contract interface. Mintplex assumes no responsibility for functionality or security.
          abstract contract ERC721ARedemption is ERC721A {
            // @dev Emitted when someone exchanges an NFT for this contracts NFT via token redemption swap
            event Redeemed(address indexed from, uint256 indexed tokenId, address indexed contractAddress);
          
            // @dev Emitted when someone proves ownership of an NFT for this contracts NFT via token redemption swap
            event VerifiedClaim(address indexed from, uint256 indexed tokenId, address indexed contractAddress);
            
            uint256 public redemptionSurcharge = 0 ether;
            bool public redemptionModeEnabled;
            bool public verifiedClaimModeEnabled;
            address public redemptionAddress = 0x000000000000000000000000000000000000dEaD; // address burned tokens are sent, default is dEaD.
            mapping(address => bool) public redemptionContracts;
            mapping(address => mapping(uint256 => bool)) public tokenRedemptions;
          
            // @dev Allow owner/team to set the contract as eligable for redemption for this contract
            function setRedeemableContract(address _contractAddress, bool _status) public onlyTeamOrOwner {
              redemptionContracts[_contractAddress] = _status;
            }
          
            // @dev Allow owner/team to determine if contract is accepting redemption mints
            function setRedemptionMode(bool _newStatus) public onlyTeamOrOwner {
              redemptionModeEnabled = _newStatus;
            }
          
            // @dev Allow owner/team to determine if contract is accepting verified claim mints
            function setVerifiedClaimMode(bool _newStatus) public onlyTeamOrOwner {
              verifiedClaimModeEnabled = _newStatus;
            }
          
            // @dev Set the fee that it would cost a minter to be able to burn/validtion mint a token on this contract. 
            function setRedemptionSurcharge(uint256 _newSurchargeInWei) public onlyTeamOrOwner {
              redemptionSurcharge = _newSurchargeInWei;
            }
          
            // @dev Set the redemption address where redeemed NFTs will be transferred when "burned". 
            // @notice Must be a wallet address or implement IERC721Receiver.
            // Cannot be null address as this will break any ERC-721A implementation without a proper
            // burn mechanic as ownershipOf cannot handle 0x00 holdings mid batch.
            function setRedemptionAddress(address _newRedemptionAddress) public onlyTeamOrOwner {
              if(_newRedemptionAddress == address(0)) revert CannotBeNullAddress();
              redemptionAddress = _newRedemptionAddress;
            }
          
            /**
            * @dev allows redemption or "burning" of a single tokenID. Must be owned by the owner
            * @notice this does not impact the total supply of the burned token and the transfer destination address may be set by
            * the contract owner or Team => redemptionAddress. 
            * @param tokenId the token to be redeemed.
            * Emits a {Redeemed} event.
            **/
            function redeem(address redemptionContract, uint256 tokenId) public payable {
              if(getNextTokenId() > collectionSize) revert CapExceeded();
              if(!redemptionModeEnabled) revert ClaimModeDisabled();
              if(redemptionContract == address(0)) revert CannotBeNullAddress();
              if(!redemptionContracts[redemptionContract]) revert IneligibleRedemptionContract();
              if(msg.value != redemptionSurcharge) revert InvalidPayment();
              if(tokenRedemptions[redemptionContract][tokenId]) revert TokenAlreadyRedeemed();
              
              IERC721 _targetContract = IERC721(redemptionContract);
              if(_targetContract.ownerOf(tokenId) != _msgSender()) revert InvalidOwnerForRedemption();
              if(_targetContract.getApproved(tokenId) != address(this)) revert InvalidApprovalForRedemption();
              
              // Warning: Since there is no standarized return value for transfers of ERC-721
              // It is possible this function silently fails and a mint still occurs. The owner of the contract is
              // responsible for ensuring that the redemption contract does not lock or have staked controls preventing
              // movement of the token. As an added measure we keep a mapping of tokens redeemed to prevent multiple single-token redemptions, 
              // but the NFT may not have been sent to the redemptionAddress.
              _targetContract.safeTransferFrom(_msgSender(), redemptionAddress, tokenId);
              tokenRedemptions[redemptionContract][tokenId] = true;
          
              emit Redeemed(_msgSender(), tokenId, redemptionContract);
              _safeMint(_msgSender(), 1, false);
            }
          
            /**
            * @dev allows for verified claim mint against a single tokenID. Must be owned by the owner
            * @notice this mint action allows the original NFT to remain in the holders wallet, but its claim is logged.
            * @param tokenId the token to be redeemed.
            * Emits a {VerifiedClaim} event.
            **/
            function verifedClaim(address redemptionContract, uint256 tokenId) public payable {
              if(getNextTokenId() > collectionSize) revert CapExceeded();
              if(!verifiedClaimModeEnabled) revert ClaimModeDisabled();
              if(redemptionContract == address(0)) revert CannotBeNullAddress();
              if(!redemptionContracts[redemptionContract]) revert IneligibleRedemptionContract();
              if(msg.value != redemptionSurcharge) revert InvalidPayment();
              if(tokenRedemptions[redemptionContract][tokenId]) revert TokenAlreadyRedeemed();
              
              tokenRedemptions[redemptionContract][tokenId] = true;
              emit VerifiedClaim(_msgSender(), tokenId, redemptionContract);
              _safeMint(_msgSender(), 1, false);
            }
          }
          
          
            
          /** TimedDrop.sol
          * This feature will allow the owner to be able to set timed drops for both the public and allowlist mint (if applicable).
          * It is bound by the block timestamp. The owner is able to determine if the feature should be used as all 
          * with the "enforcePublicDropTime" and "enforceAllowlistDropTime" variables. If the feature is disabled the implmented
          * *DropTimePassed() functions will always return true. Otherwise calculation is done to check if time has passed.
          */
          abstract contract TimedDrop is Teams {
            bool public enforcePublicDropTime = true;
            uint256 public publicDropTime = 1675054800;
            
            /**
            * @dev Allow the contract owner to set the public time to mint.
            * @param _newDropTime timestamp since Epoch in seconds you want public drop to happen
            */
            function setPublicDropTime(uint256 _newDropTime) public onlyTeamOrOwner {
              if(_newDropTime < block.timestamp) revert DropTimeNotInFuture();
              publicDropTime = _newDropTime;
            }
          
            function usePublicDropTime() public onlyTeamOrOwner {
              enforcePublicDropTime = true;
            }
          
            function disablePublicDropTime() public onlyTeamOrOwner {
              enforcePublicDropTime = false;
            }
          
            /**
            * @dev determine if the public droptime has passed.
            * if the feature is disabled then assume the time has passed.
            */
            function publicDropTimePassed() public view returns(bool) {
              if(enforcePublicDropTime == false) {
                return true;
              }
              return block.timestamp >= publicDropTime;
            }
            
            // Allowlist implementation of the Timed Drop feature
            bool public enforceAllowlistDropTime = true;
            uint256 public allowlistDropTime = 1675029600;
          
            /**
            * @dev Allow the contract owner to set the allowlist time to mint.
            * @param _newDropTime timestamp since Epoch in seconds you want public drop to happen
            */
            function setAllowlistDropTime(uint256 _newDropTime) public onlyTeamOrOwner {
              if(_newDropTime < block.timestamp) revert DropTimeNotInFuture();
              allowlistDropTime = _newDropTime;
            }
          
            function useAllowlistDropTime() public onlyTeamOrOwner {
              enforceAllowlistDropTime = true;
            }
          
            function disableAllowlistDropTime() public onlyTeamOrOwner {
              enforceAllowlistDropTime = false;
            }
          
            function allowlistDropTimePassed() public view returns(bool) {
              if(enforceAllowlistDropTime == false) {
                return true;
              }
          
              return block.timestamp >= allowlistDropTime;
            }
          }
          
            
          interface IERC20 {
            function allowance(address owner, address spender) external view returns (uint256);
            function transfer(address _to, uint256 _amount) external returns (bool);
            function balanceOf(address account) external view returns (uint256);
            function transferFrom(address from, address to, uint256 amount) external returns (bool);
          }
          
          // File: WithdrawableV2
          // This abstract allows the contract to be able to mint and ingest ERC-20 payments for mints.
          // ERC-20 Payouts are limited to a single payout address. This feature 
          // will charge a small flat fee in native currency that is not subject to regular rev sharing.
          // This contract also covers the normal functionality of accepting native base currency rev-sharing
          abstract contract WithdrawableV2 is Teams {
            struct acceptedERC20 {
              bool isActive;
              uint256 chargeAmount;
            }
          
            
            mapping(address => acceptedERC20) private allowedTokenContracts;
            address[] public payableAddresses = [0x21c87dE6AB8C127b494349Cd2dE13e4f87424CdD,0x7e5aa86d96f2F2f047afe6577033c0d6C093D924];
            address public erc20Payable = 0x21c87dE6AB8C127b494349Cd2dE13e4f87424CdD;
            uint256[] public payableFees = [75,25];
            uint256 public payableAddressCount = 2;
            bool public onlyERC20MintingMode;
            
          
            function withdrawAll() public onlyTeamOrOwner {
                if(address(this).balance == 0) revert ValueCannotBeZero();
                _withdrawAll(address(this).balance);
            }
          
            function _withdrawAll(uint256 balance) private {
                for(uint i=0; i < payableAddressCount; i++ ) {
                    _widthdraw(
                        payableAddresses[i],
                        (balance * payableFees[i]) / 100
                    );
                }
            }
            
            function _widthdraw(address _address, uint256 _amount) private {
                (bool success, ) = _address.call{value: _amount}("");
                require(success, "Transfer failed.");
            }
          
            /**
            * @dev Allow contract owner to withdraw ERC-20 balance from contract
            * in the event ERC-20 tokens are paid to the contract for mints.
            * @param _tokenContract contract of ERC-20 token to withdraw
            * @param _amountToWithdraw balance to withdraw according to balanceOf of ERC-20 token in wei
            */
            function withdrawERC20(address _tokenContract, uint256 _amountToWithdraw) public onlyTeamOrOwner {
              if(_amountToWithdraw == 0) revert ValueCannotBeZero();
              IERC20 tokenContract = IERC20(_tokenContract);
              if(tokenContract.balanceOf(address(this)) < _amountToWithdraw) revert ERC20InsufficientBalance();
              tokenContract.transfer(erc20Payable, _amountToWithdraw); // Payout ERC-20 tokens to recipient
            }
          
            /**
            * @dev check if an ERC-20 contract is a valid payable contract for executing a mint.
            * @param _erc20TokenContract address of ERC-20 contract in question
            */
            function isApprovedForERC20Payments(address _erc20TokenContract) public view returns(bool) {
              return allowedTokenContracts[_erc20TokenContract].isActive == true;
            }
          
            /**
            * @dev get the value of tokens to transfer for user of an ERC-20
            * @param _erc20TokenContract address of ERC-20 contract in question
            */
            function chargeAmountForERC20(address _erc20TokenContract) public view returns(uint256) {
              if(!isApprovedForERC20Payments(_erc20TokenContract)) revert ERC20TokenNotApproved();
              return allowedTokenContracts[_erc20TokenContract].chargeAmount;
            }
          
            /**
            * @dev Explicity sets and ERC-20 contract as an allowed payment method for minting
            * @param _erc20TokenContract address of ERC-20 contract in question
            * @param _isActive default status of if contract should be allowed to accept payments
            * @param _chargeAmountInTokens fee (in tokens) to charge for mints for this specific ERC-20 token
            */
            function addOrUpdateERC20ContractAsPayment(address _erc20TokenContract, bool _isActive, uint256 _chargeAmountInTokens) public onlyTeamOrOwner {
              allowedTokenContracts[_erc20TokenContract].isActive = _isActive;
              allowedTokenContracts[_erc20TokenContract].chargeAmount = _chargeAmountInTokens;
            }
          
            /**
            * @dev Add an ERC-20 contract as being a valid payment method. If passed a contract which has not been added
            * it will assume the default value of zero. This should not be used to create new payment tokens.
            * @param _erc20TokenContract address of ERC-20 contract in question
            */
            function enableERC20ContractAsPayment(address _erc20TokenContract) public onlyTeamOrOwner {
              allowedTokenContracts[_erc20TokenContract].isActive = true;
            }
          
            /**
            * @dev Disable an ERC-20 contract as being a valid payment method. If passed a contract which has not been added
            * it will assume the default value of zero. This should not be used to create new payment tokens.
            * @param _erc20TokenContract address of ERC-20 contract in question
            */
            function disableERC20ContractAsPayment(address _erc20TokenContract) public onlyTeamOrOwner {
              allowedTokenContracts[_erc20TokenContract].isActive = false;
            }
          
            /**
            * @dev Enable only ERC-20 payments for minting on this contract
            */
            function enableERC20OnlyMinting() public onlyTeamOrOwner {
              onlyERC20MintingMode = true;
            }
          
            /**
            * @dev Disable only ERC-20 payments for minting on this contract
            */
            function disableERC20OnlyMinting() public onlyTeamOrOwner {
              onlyERC20MintingMode = false;
            }
          
            /**
            * @dev Set the payout of the ERC-20 token payout to a specific address
            * @param _newErc20Payable new payout addresses of ERC-20 tokens
            */
            function setERC20PayableAddress(address _newErc20Payable) public onlyTeamOrOwner {
              if(_newErc20Payable == address(0)) revert CannotBeNullAddress();
              if(_newErc20Payable == erc20Payable) revert NoStateChange();
              erc20Payable = _newErc20Payable;
            }
          }
          
          
            
            
            
          // File: EarlyMintIncentive.sol
          // Allows the contract to have the first x tokens minted for a wallet at a discount or
          // zero fee that can be calculated on the fly.
          abstract contract EarlyMintIncentive is Teams, ERC721A {
            uint256 public PRICE = 0.008 ether;
            uint256 public EARLY_MINT_PRICE = 0 ether;
            uint256 public earlyMintOwnershipCap = 1;
            bool public usingEarlyMintIncentive = true;
          
            function enableEarlyMintIncentive() public onlyTeamOrOwner {
              usingEarlyMintIncentive = true;
            }
          
            function disableEarlyMintIncentive() public onlyTeamOrOwner {
              usingEarlyMintIncentive = false;
            }
          
            /**
            * @dev Set the max token ID in which the cost incentive will be applied.
            * @param _newCap max number of tokens wallet may mint for incentive price
            */
            function setEarlyMintOwnershipCap(uint256 _newCap) public onlyTeamOrOwner {
              if(_newCap == 0) revert ValueCannotBeZero();
              earlyMintOwnershipCap = _newCap;
            }
          
            /**
            * @dev Set the incentive mint price
            * @param _feeInWei new price per token when in incentive range
            */
            function setEarlyIncentivePrice(uint256 _feeInWei) public onlyTeamOrOwner {
              EARLY_MINT_PRICE = _feeInWei;
            }
          
            /**
            * @dev Set the primary mint price - the base price when not under incentive
            * @param _feeInWei new price per token
            */
            function setPrice(uint256 _feeInWei) public onlyTeamOrOwner {
              PRICE = _feeInWei;
            }
          
            /**
            * @dev Get the correct price for the mint for qty and person minting
            * @param _count amount of tokens to calc for mint
            * @param _to the address which will be minting these tokens, passed explicitly
            */
            function getPrice(uint256 _count, address _to) public view returns (uint256) {
              if(_count == 0) revert ValueCannotBeZero();
          
              // short circuit function if we dont need to even calc incentive pricing
              // short circuit if the current wallet mint qty is also already over cap
              if(
                usingEarlyMintIncentive == false ||
                _numberMinted(_to) > earlyMintOwnershipCap
              ) {
                return PRICE * _count;
              }
          
              uint256 endingTokenQty = _numberMinted(_to) + _count;
              // If qty to mint results in a final qty less than or equal to the cap then
              // the entire qty is within incentive mint.
              if(endingTokenQty  <= earlyMintOwnershipCap) {
                return EARLY_MINT_PRICE * _count;
              }
          
              // If the current token qty is less than the incentive cap
              // and the ending token qty is greater than the incentive cap
              // we will be straddling the cap so there will be some amount
              // that are incentive and some that are regular fee.
              uint256 incentiveTokenCount = earlyMintOwnershipCap - _numberMinted(_to);
              uint256 outsideIncentiveCount = endingTokenQty - earlyMintOwnershipCap;
          
              return (EARLY_MINT_PRICE * incentiveTokenCount) + (PRICE * outsideIncentiveCount);
            }
          }
          
            
          abstract contract RamppERC721A is 
              Ownable,
              Teams,
              ERC721ARedemption,
              WithdrawableV2,
              ReentrancyGuard 
              , EarlyMintIncentive 
              , Allowlist 
              , TimedDrop
          {
            constructor(
              string memory tokenName,
              string memory tokenSymbol
            ) ERC721A(tokenName, tokenSymbol, 5, 8888) { }
              uint8 constant public CONTRACT_VERSION = 2;
              string public _baseTokenURI = "ipfs://bafybeiffpzh35wcz46kgfvqs2h4tz2jw2hv3kzmiedajaxedxp43qv6n7e/";
              string public _baseTokenExtension = ".json";
          
              bool public mintingOpen = false;
              bool public isRevealed;
              
              uint256 public MAX_WALLET_MINTS = 5;
          
            
              /////////////// Admin Mint Functions
              /**
               * @dev Mints a token to an address with a tokenURI.
               * This is owner only and allows a fee-free drop
               * @param _to address of the future owner of the token
               * @param _qty amount of tokens to drop the owner
               */
               function mintToAdminV2(address _to, uint256 _qty) public onlyTeamOrOwner{
                   if(_qty == 0) revert MintZeroQuantity();
                   if(currentTokenId() + _qty > collectionSize) revert CapExceeded();
                   _safeMint(_to, _qty, true);
               }
          
            
              /////////////// PUBLIC MINT FUNCTIONS
              /**
              * @dev Mints tokens to an address in batch.
              * fee may or may not be required*
              * @param _to address of the future owner of the token
              * @param _amount number of tokens to mint
              */
              function mintToMultiple(address _to, uint256 _amount) public payable {
                  if(onlyERC20MintingMode) revert OnlyERC20MintingEnabled();
                  if(_amount == 0) revert MintZeroQuantity();
                  if(_amount > maxBatchSize) revert TransactionCapExceeded();
                  if(!mintingOpen) revert PublicMintClosed();
                  if(mintingOpen && onlyAllowlistMode) revert PublicMintClosed();
                  if(!publicDropTimePassed()) revert PublicDropTimeHasNotPassed();
                  if(!canMintAmount(_to, _amount)) revert ExcessiveOwnedMints();
                  if(currentTokenId() + _amount > collectionSize) revert CapExceeded();
                  if(msg.value != getPrice(_amount, _to)) revert InvalidPayment();
          
                  _safeMint(_to, _amount, false);
              }
          
              /**
               * @dev Mints tokens to an address in batch using an ERC-20 token for payment
               * fee may or may not be required*
               * @param _to address of the future owner of the token
               * @param _amount number of tokens to mint
               * @param _erc20TokenContract erc-20 token contract to mint with
               */
              function mintToMultipleERC20(address _to, uint256 _amount, address _erc20TokenContract) public payable {
                if(_amount == 0) revert MintZeroQuantity();
                if(_amount > maxBatchSize) revert TransactionCapExceeded();
                if(!mintingOpen) revert PublicMintClosed();
                if(currentTokenId() + _amount > collectionSize) revert CapExceeded();
                if(mintingOpen && onlyAllowlistMode) revert PublicMintClosed();
                if(!publicDropTimePassed()) revert PublicDropTimeHasNotPassed();
                if(!canMintAmount(_to, _amount)) revert ExcessiveOwnedMints();
          
                // ERC-20 Specific pre-flight checks
                if(!isApprovedForERC20Payments(_erc20TokenContract)) revert ERC20TokenNotApproved();
                uint256 tokensQtyToTransfer = chargeAmountForERC20(_erc20TokenContract) * _amount;
                IERC20 payableToken = IERC20(_erc20TokenContract);
          
                if(payableToken.balanceOf(_to) < tokensQtyToTransfer) revert ERC20InsufficientBalance();
                if(payableToken.allowance(_to, address(this)) < tokensQtyToTransfer) revert ERC20InsufficientAllowance();
          
                bool transferComplete = payableToken.transferFrom(_to, address(this), tokensQtyToTransfer);
                if(!transferComplete) revert ERC20TransferFailed();
                
                _safeMint(_to, _amount, false);
              }
          
              function openMinting() public onlyTeamOrOwner {
                  mintingOpen = true;
              }
          
              function stopMinting() public onlyTeamOrOwner {
                  mintingOpen = false;
              }
          
            
              ///////////// ALLOWLIST MINTING FUNCTIONS
              /**
              * @dev Mints tokens to an address using an allowlist.
              * fee may or may not be required*
              * @param _to address of the future owner of the token
              * @param _amount number of tokens to mint
              * @param _merkleProof merkle proof array
              */
              function mintToMultipleAL(address _to, uint256 _amount, bytes32[] calldata _merkleProof) public payable {
                  if(onlyERC20MintingMode) revert OnlyERC20MintingEnabled();
                  if(!onlyAllowlistMode || !mintingOpen) revert AllowlistMintClosed();
                  if(!isAllowlisted(_to, _merkleProof)) revert AddressNotAllowlisted();
                  if(_amount == 0) revert MintZeroQuantity();
                  if(_amount > maxBatchSize) revert TransactionCapExceeded();
                  if(!canMintAmount(_to, _amount)) revert ExcessiveOwnedMints();
                  if(currentTokenId() + _amount > collectionSize) revert CapExceeded();
                  if(msg.value != getPrice(_amount, _to)) revert InvalidPayment();
                  if(!allowlistDropTimePassed()) revert AllowlistDropTimeHasNotPassed();
          
                  _safeMint(_to, _amount, false);
              }
          
              /**
              * @dev Mints tokens to an address using an allowlist.
              * fee may or may not be required*
              * @param _to address of the future owner of the token
              * @param _amount number of tokens to mint
              * @param _merkleProof merkle proof array
              * @param _erc20TokenContract erc-20 token contract to mint with
              */
              function mintToMultipleERC20AL(address _to, uint256 _amount, bytes32[] calldata _merkleProof, address _erc20TokenContract) public payable {
                if(!onlyAllowlistMode || !mintingOpen) revert AllowlistMintClosed();
                if(!isAllowlisted(_to, _merkleProof)) revert AddressNotAllowlisted();
                if(_amount == 0) revert MintZeroQuantity();
                if(_amount > maxBatchSize) revert TransactionCapExceeded();
                if(!canMintAmount(_to, _amount)) revert ExcessiveOwnedMints();
                if(currentTokenId() + _amount > collectionSize) revert CapExceeded();
                if(!allowlistDropTimePassed()) revert AllowlistDropTimeHasNotPassed();
              
                // ERC-20 Specific pre-flight checks
                if(!isApprovedForERC20Payments(_erc20TokenContract)) revert ERC20TokenNotApproved();
                uint256 tokensQtyToTransfer = chargeAmountForERC20(_erc20TokenContract) * _amount;
                IERC20 payableToken = IERC20(_erc20TokenContract);
          
                if(payableToken.balanceOf(_to) < tokensQtyToTransfer) revert ERC20InsufficientBalance();
                if(payableToken.allowance(_to, address(this)) < tokensQtyToTransfer) revert ERC20InsufficientAllowance();
          
                bool transferComplete = payableToken.transferFrom(_to, address(this), tokensQtyToTransfer);
                if(!transferComplete) revert ERC20TransferFailed();
                
                _safeMint(_to, _amount, false);
              }
          
              /**
               * @dev Enable allowlist minting fully by enabling both flags
               * This is a convenience function for the Rampp user
               */
              function openAllowlistMint() public onlyTeamOrOwner {
                  enableAllowlistOnlyMode();
                  mintingOpen = true;
              }
          
              /**
               * @dev Close allowlist minting fully by disabling both flags
               * This is a convenience function for the Rampp user
               */
              function closeAllowlistMint() public onlyTeamOrOwner {
                  disableAllowlistOnlyMode();
                  mintingOpen = false;
              }
          
          
            
              /**
              * @dev Check if wallet over MAX_WALLET_MINTS
              * @param _address address in question to check if minted count exceeds max
              */
              function canMintAmount(address _address, uint256 _amount) public view returns(bool) {
                  if(_amount == 0) revert ValueCannotBeZero();
                  return (_numberMinted(_address) + _amount) <= MAX_WALLET_MINTS;
              }
          
              /**
              * @dev Update the maximum amount of tokens that can be minted by a unique wallet
              * @param _newWalletMax the new max of tokens a wallet can mint. Must be >= 1
              */
              function setWalletMax(uint256 _newWalletMax) public onlyTeamOrOwner {
                  if(_newWalletMax == 0) revert ValueCannotBeZero();
                  MAX_WALLET_MINTS = _newWalletMax;
              }
              
          
            
              /**
               * @dev Allows owner to set Max mints per tx
               * @param _newMaxMint maximum amount of tokens allowed to mint per tx. Must be >= 1
               */
               function setMaxMint(uint256 _newMaxMint) public onlyTeamOrOwner {
                   if(_newMaxMint == 0) revert ValueCannotBeZero();
                   maxBatchSize = _newMaxMint;
               }
              
          
            
              function unveil(string memory _updatedTokenURI) public onlyTeamOrOwner {
                  if(isRevealed) revert IsAlreadyUnveiled();
                  _baseTokenURI = _updatedTokenURI;
                  isRevealed = true;
              }
              
            
            
            function contractURI() public pure returns (string memory) {
              return "https://metadata.mintplex.xyz/y86AeB1hIL0bFKGf9Kgw/contract-metadata";
            }
            
          
            function _baseURI() internal view virtual override returns(string memory) {
              return _baseTokenURI;
            }
          
            function _baseURIExtension() internal view virtual override returns(string memory) {
              return _baseTokenExtension;
            }
          
            function baseTokenURI() public view returns(string memory) {
              return _baseTokenURI;
            }
          
            function setBaseURI(string calldata baseURI) external onlyTeamOrOwner {
              _baseTokenURI = baseURI;
            }
          
            function setBaseTokenExtension(string calldata baseExtension) external onlyTeamOrOwner {
              _baseTokenExtension = baseExtension;
            }
          }
          
          
            
          // File: contracts/ChungosContract.sol
          //SPDX-License-Identifier: MIT
          
          pragma solidity ^0.8.0;
          
          contract ChungosContract is RamppERC721A {
              constructor() RamppERC721A("Chungos", "CHUNGO"){}
          }
            

          File 2 of 4: BlurExchange
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
          import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
          import "./lib/ReentrancyGuarded.sol";
          import "./lib/EIP712.sol";
          import "./lib/MerkleVerifier.sol";
          import "./interfaces/IBlurExchange.sol";
          import "./interfaces/IBlurPool.sol";
          import "./interfaces/IExecutionDelegate.sol";
          import "./interfaces/IPolicyManager.sol";
          import "./interfaces/IMatchingPolicy.sol";
          import {
            Side,
            SignatureVersion,
            AssetType,
            Fee,
            Order,
            Input,
            Execution
          } from "./lib/OrderStructs.sol";
          /**
           * @title BlurExchange
           * @dev Core Blur exchange contract
           */
          contract BlurExchange is IBlurExchange, ReentrancyGuarded, EIP712, OwnableUpgradeable, UUPSUpgradeable {
              /* Auth */
              uint256 public isOpen;
              modifier whenOpen() {
                  require(isOpen == 1, "Closed");
                  _;
              }
              modifier setupExecution() {
                  require(!isInternal, "Unsafe call"); // add redundant re-entrancy check for clarity
                  remainingETH = msg.value;
                  isInternal = true;
                  _;
                  remainingETH = 0;
                  isInternal = false;
              }
              modifier internalCall() {
                  require(isInternal, "Unsafe call");
                  _;
              }
              event Opened();
              event Closed();
              function open() external onlyOwner {
                  isOpen = 1;
                  emit Opened();
              }
              function close() external onlyOwner {
                  isOpen = 0;
                  emit Closed();
              }
              // required by the OZ UUPS module
              function _authorizeUpgrade(address) internal override onlyOwner {}
              /* Constants */
              string public constant NAME = "Blur Exchange";
              string public constant VERSION = "1.0";
              uint256 public constant INVERSE_BASIS_POINT = 10_000;
              address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
              address public constant POOL = 0x0000000000A39bb272e79075ade125fd351887Ac;
              uint256 private constant MAX_FEE_RATE = 250;
              /* Variables */
              IExecutionDelegate public executionDelegate;
              IPolicyManager public policyManager;
              address public oracle;
              uint256 public blockRange;
              /* Storage */
              mapping(bytes32 => bool) public cancelledOrFilled;
              mapping(address => uint256) public nonces;
              bool public isInternal = false;
              uint256 public remainingETH = 0;
              /* Governance Variables */
              uint256 public feeRate;
              address public feeRecipient;
              address public governor;
              /* Events */
              event OrdersMatched(
                  address indexed maker,
                  address indexed taker,
                  Order sell,
                  bytes32 sellHash,
                  Order buy,
                  bytes32 buyHash
              );
              event OrderCancelled(bytes32 hash);
              event NonceIncremented(address indexed trader, uint256 newNonce);
              event NewExecutionDelegate(IExecutionDelegate indexed executionDelegate);
              event NewPolicyManager(IPolicyManager indexed policyManager);
              event NewOracle(address indexed oracle);
              event NewBlockRange(uint256 blockRange);
              event NewFeeRate(uint256 feeRate);
              event NewFeeRecipient(address feeRecipient);
              event NewGovernor(address governor);
              constructor() {
                _disableInitializers();
              }
              /* Constructor (for ERC1967) */
              function initialize(
                  IExecutionDelegate _executionDelegate,
                  IPolicyManager _policyManager,
                  address _oracle,
                  uint _blockRange
              ) external initializer {
                  __Ownable_init();
                  isOpen = 1;
                  DOMAIN_SEPARATOR = _hashDomain(EIP712Domain({
                      name              : NAME,
                      version           : VERSION,
                      chainId           : block.chainid,
                      verifyingContract : address(this)
                  }));
                  executionDelegate = _executionDelegate;
                  policyManager = _policyManager;
                  oracle = _oracle;
                  blockRange = _blockRange;
              }
              /* External Functions */
              /**
               * @dev _execute wrapper
               * @param sell Sell input
               * @param buy Buy input
               */
              function execute(Input calldata sell, Input calldata buy)
                  external
                  payable
                  whenOpen
                  setupExecution
              {
                  _execute(sell, buy);
                  _returnDust();
              }
              /**
               * @dev Bulk execute multiple matches
               * @param executions Potential buy/sell matches
               */
              function bulkExecute(Execution[] calldata executions)
                  external
                  payable
                  whenOpen
                  setupExecution
              {
                  /*
                  REFERENCE
                  uint256 executionsLength = executions.length;
                  for (uint8 i=0; i < executionsLength; i++) {
                      bytes memory data = abi.encodeWithSelector(this._execute.selector, executions[i].sell, executions[i].buy);
                      (bool success,) = address(this).delegatecall(data);
                  }
                  _returnDust(remainingETH);
                  */
                  uint256 executionsLength = executions.length;
                  if (executionsLength == 0) {
                    revert("No orders to execute");
                  }
                  for (uint8 i = 0; i < executionsLength; i++) {
                      assembly {
                          let memPointer := mload(0x40)
                          let order_location := calldataload(add(executions.offset, mul(i, 0x20)))
                          let order_pointer := add(executions.offset, order_location)
                          let size
                          switch eq(add(i, 0x01), executionsLength)
                          case 1 {
                              size := sub(calldatasize(), order_pointer)
                          }
                          default {
                              let next_order_location := calldataload(add(executions.offset, mul(add(i, 0x01), 0x20)))
                              let next_order_pointer := add(executions.offset, next_order_location)
                              size := sub(next_order_pointer, order_pointer)
                          }
                          mstore(memPointer, 0xe04d94ae00000000000000000000000000000000000000000000000000000000) // _execute
                          calldatacopy(add(0x04, memPointer), order_pointer, size)
                          // must be put in separate transaction to bypass failed executions
                          // must be put in delegatecall to maintain the authorization from the caller
                          let result := delegatecall(gas(), address(), memPointer, add(size, 0x04), 0, 0)
                      }
                  }
                  _returnDust();
              }
              /**
               * @dev Match two orders, ensuring validity of the match, and execute all associated state transitions. Must be called internally.
               * @param sell Sell input
               * @param buy Buy input
               */
              function _execute(Input calldata sell, Input calldata buy)
                  public
                  payable
                  internalCall
                  reentrancyGuard // move re-entrancy check for clarity
              {
                  require(sell.order.side == Side.Sell);
                  bytes32 sellHash = _hashOrder(sell.order, nonces[sell.order.trader]);
                  bytes32 buyHash = _hashOrder(buy.order, nonces[buy.order.trader]);
                  require(_validateOrderParameters(sell.order, sellHash), "Sell has invalid parameters");
                  require(_validateOrderParameters(buy.order, buyHash), "Buy has invalid parameters");
                  require(_validateSignatures(sell, sellHash), "Sell failed authorization");
                  require(_validateSignatures(buy, buyHash), "Buy failed authorization");
                  (uint256 price, uint256 tokenId, uint256 amount, AssetType assetType) = _canMatchOrders(sell.order, buy.order);
                  /* Mark orders as filled. */
                  cancelledOrFilled[sellHash] = true;
                  cancelledOrFilled[buyHash] = true;
                  _executeFundsTransfer(
                      sell.order.trader,
                      buy.order.trader,
                      sell.order.paymentToken,
                      sell.order.fees,
                      buy.order.fees,
                      price
                  );
                  _executeTokenTransfer(
                      sell.order.collection,
                      sell.order.trader,
                      buy.order.trader,
                      tokenId,
                      amount,
                      assetType
                  );
                  emit OrdersMatched(
                      sell.order.listingTime <= buy.order.listingTime ? sell.order.trader : buy.order.trader,
                      sell.order.listingTime > buy.order.listingTime ? sell.order.trader : buy.order.trader,
                      sell.order,
                      sellHash,
                      buy.order,
                      buyHash
                  );
              }
              /**
               * @dev Cancel an order, preventing it from being matched. Must be called by the trader of the order
               * @param order Order to cancel
               */
              function cancelOrder(Order calldata order) public {
                  /* Assert sender is authorized to cancel order. */
                  require(msg.sender == order.trader, "Not sent by trader");
                  bytes32 hash = _hashOrder(order, nonces[order.trader]);
                  require(!cancelledOrFilled[hash], "Order cancelled or filled");
                  /* Mark order as cancelled, preventing it from being matched. */
                  cancelledOrFilled[hash] = true;
                  emit OrderCancelled(hash);
              }
              /**
               * @dev Cancel multiple orders
               * @param orders Orders to cancel
               */
              function cancelOrders(Order[] calldata orders) external {
                  for (uint8 i = 0; i < orders.length; i++) {
                      cancelOrder(orders[i]);
                  }
              }
              /**
               * @dev Cancel all current orders for a user, preventing them from being matched. Must be called by the trader of the order
               */
              function incrementNonce() external {
                  nonces[msg.sender] += 1;
                  emit NonceIncremented(msg.sender, nonces[msg.sender]);
              }
              /* Setters */
              function setExecutionDelegate(IExecutionDelegate _executionDelegate)
                  external
                  onlyOwner
              {
                  require(address(_executionDelegate) != address(0), "Address cannot be zero");
                  executionDelegate = _executionDelegate;
                  emit NewExecutionDelegate(executionDelegate);
              }
              function setPolicyManager(IPolicyManager _policyManager)
                  external
                  onlyOwner
              {
                  require(address(_policyManager) != address(0), "Address cannot be zero");
                  policyManager = _policyManager;
                  emit NewPolicyManager(policyManager);
              }
              function setOracle(address _oracle)
                  external
                  onlyOwner
              {
                  require(_oracle != address(0), "Address cannot be zero");
                  oracle = _oracle;
                  emit NewOracle(oracle);
              }
              function setBlockRange(uint256 _blockRange)
                  external
                  onlyOwner
              {
                  blockRange = _blockRange;
                  emit NewBlockRange(blockRange);
              }
              function setGovernor(address _governor)
                  external
                  onlyOwner
              {
                  governor = _governor;
                  emit NewGovernor(governor);
              }
              function setFeeRate(uint256 _feeRate)
                  external
              {
                  require(msg.sender == governor, "Fee rate can only be set by governor");
                  require(_feeRate <= MAX_FEE_RATE, "Fee cannot be more than 2.5%");
                  feeRate = _feeRate;
                  emit NewFeeRate(feeRate);
              }
              function setFeeRecipient(address _feeRecipient)
                  external
                  onlyOwner
              {
                  feeRecipient = _feeRecipient;
                  emit NewFeeRecipient(feeRecipient);
              }
              /* Internal Functions */
              /**
               * @dev Verify the validity of the order parameters
               * @param order order
               * @param orderHash hash of order
               */
              function _validateOrderParameters(Order calldata order, bytes32 orderHash)
                  internal
                  view
                  returns (bool)
              {
                  return (
                      /* Order must have a trader. */
                      (order.trader != address(0)) &&
                      /* Order must not be cancelled or filled. */
                      (!cancelledOrFilled[orderHash]) &&
                      /* Order must be settleable. */
                      (order.listingTime < block.timestamp) &&
                      (block.timestamp < order.expirationTime)
                  );
              }
              /**
               * @dev Verify the validity of the signatures
               * @param order order
               * @param orderHash hash of order
               */
              function _validateSignatures(Input calldata order, bytes32 orderHash)
                  internal
                  view
                  returns (bool)
              {
                  if (order.order.extraParams.length > 0 && order.order.extraParams[0] == 0x01) {
                      /* Check oracle authorization. */
                      require(block.number - order.blockNumber < blockRange, "Signed block number out of range");
                      if (
                          !_validateOracleAuthorization(
                              orderHash,
                              order.signatureVersion,
                              order.extraSignature,
                              order.blockNumber
                          )
                      ) {
                          return false;
                      }
                  }
                  if (order.order.trader == msg.sender) {
                    return true;
                  }
                  /* Check user authorization. */
                  if (
                      !_validateUserAuthorization(
                          orderHash,
                          order.order.trader,
                          order.v,
                          order.r,
                          order.s,
                          order.signatureVersion,
                          order.extraSignature
                      )
                  ) {
                      return false;
                  }
                  return true;
              }
              /**
               * @dev Verify the validity of the user signature
               * @param orderHash hash of the order
               * @param trader order trader who should be the signer
               * @param v v
               * @param r r
               * @param s s
               * @param signatureVersion signature version
               * @param extraSignature packed merkle path
               */
              function _validateUserAuthorization(
                  bytes32 orderHash,
                  address trader,
                  uint8 v,
                  bytes32 r,
                  bytes32 s,
                  SignatureVersion signatureVersion,
                  bytes calldata extraSignature
              ) internal view returns (bool) {
                  bytes32 hashToSign;
                  if (signatureVersion == SignatureVersion.Single) {
                      /* Single-listing authentication: Order signed by trader */
                      hashToSign = _hashToSign(orderHash);
                  } else if (signatureVersion == SignatureVersion.Bulk) {
                      /* Bulk-listing authentication: Merkle root of orders signed by trader */
                      (bytes32[] memory merklePath) = abi.decode(extraSignature, (bytes32[]));
                      bytes32 computedRoot = MerkleVerifier._computeRoot(orderHash, merklePath);
                      hashToSign = _hashToSignRoot(computedRoot);
                  }
                  return _verify(trader, hashToSign, v, r, s);
              }
              /**
               * @dev Verify the validity of oracle signature
               * @param orderHash hash of the order
               * @param signatureVersion signature version
               * @param extraSignature packed oracle signature
               * @param blockNumber block number used in oracle signature
               */
              function _validateOracleAuthorization(
                  bytes32 orderHash,
                  SignatureVersion signatureVersion,
                  bytes calldata extraSignature,
                  uint256 blockNumber
              ) internal view returns (bool) {
                  bytes32 oracleHash = _hashToSignOracle(orderHash, blockNumber);
                  uint8 v; bytes32 r; bytes32 s;
                  if (signatureVersion == SignatureVersion.Single) {
                      assembly {
                          v := calldataload(extraSignature.offset)
                          r := calldataload(add(extraSignature.offset, 0x20))
                          s := calldataload(add(extraSignature.offset, 0x40))
                      }
                      /*
                      REFERENCE
                      (v, r, s) = abi.decode(extraSignature, (uint8, bytes32, bytes32));
                      */
                  } else if (signatureVersion == SignatureVersion.Bulk) {
                      /* If the signature was a bulk listing the merkle path must be unpacked before the oracle signature. */
                      assembly {
                          v := calldataload(add(extraSignature.offset, 0x20))
                          r := calldataload(add(extraSignature.offset, 0x40))
                          s := calldataload(add(extraSignature.offset, 0x60))
                      }
                      /*
                      REFERENCE
                      uint8 _v, bytes32 _r, bytes32 _s;
                      (bytes32[] memory merklePath, uint8 _v, bytes32 _r, bytes32 _s) = abi.decode(extraSignature, (bytes32[], uint8, bytes32, bytes32));
                      v = _v; r = _r; s = _s;
                      */
                  }
                  return _verify(oracle, oracleHash, v, r, s);
              }
              /**
               * @dev Verify ECDSA signature
               * @param signer Expected signer
               * @param digest Signature preimage
               * @param v v
               * @param r r
               * @param s s
               */
              function _verify(
                  address signer,
                  bytes32 digest,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) internal pure returns (bool) {
                  require(v == 27 || v == 28, "Invalid v parameter");
                  address recoveredSigner = ecrecover(digest, v, r, s);
                  if (recoveredSigner == address(0)) {
                    return false;
                  } else {
                    return signer == recoveredSigner;
                  }
              }
              /**
               * @dev Call the matching policy to check orders can be matched and get execution parameters
               * @param sell sell order
               * @param buy buy order
               */
              function _canMatchOrders(Order calldata sell, Order calldata buy)
                  internal
                  view
                  returns (uint256 price, uint256 tokenId, uint256 amount, AssetType assetType)
              {
                  bool canMatch;
                  if (sell.listingTime <= buy.listingTime) {
                      /* Seller is maker. */
                      require(policyManager.isPolicyWhitelisted(sell.matchingPolicy), "Policy is not whitelisted");
                      (canMatch, price, tokenId, amount, assetType) = IMatchingPolicy(sell.matchingPolicy).canMatchMakerAsk(sell, buy);
                  } else {
                      /* Buyer is maker. */
                      require(policyManager.isPolicyWhitelisted(buy.matchingPolicy), "Policy is not whitelisted");
                      (canMatch, price, tokenId, amount, assetType) = IMatchingPolicy(buy.matchingPolicy).canMatchMakerBid(buy, sell);
                  }
                  require(canMatch, "Orders cannot be matched");
                  return (price, tokenId, amount, assetType);
              }
              /**
               * @dev Execute all ERC20 token / ETH transfers associated with an order match (fees and buyer => seller transfer)
               * @param seller seller
               * @param buyer buyer
               * @param paymentToken payment token
               * @param sellerFees seller fees
               * @param buyerFees buyer fees
               * @param price price
               */
              function _executeFundsTransfer(
                  address seller,
                  address buyer,
                  address paymentToken,
                  Fee[] calldata sellerFees,
                  Fee[] calldata buyerFees,
                  uint256 price
              ) internal {
                  if (paymentToken == address(0)) {
                      require(msg.sender == buyer, "Cannot use ETH");
                      require(remainingETH >= price, "Insufficient value");
                      remainingETH -= price;
                  }
                  /* Take fee. */
                  uint256 sellerFeesPaid = _transferFees(sellerFees, paymentToken, buyer, price, true);
                  uint256 buyerFeesPaid = _transferFees(buyerFees, paymentToken, buyer, price, false);
                  if (paymentToken == address(0)) {
                    /* Need to account for buyer fees paid on top of the price. */
                    remainingETH -= buyerFeesPaid;
                  }
                  /* Transfer remainder to seller. */
                  _transferTo(paymentToken, buyer, seller, price - sellerFeesPaid);
              }
              /**
               * @dev Charge a fee in ETH or WETH
               * @param fees fees to distribute
               * @param paymentToken address of token to pay in
               * @param from address to charge fees
               * @param price price of token
               * @return total fees paid
               */
              function _transferFees(
                  Fee[] calldata fees,
                  address paymentToken,
                  address from,
                  uint256 price,
                  bool protocolFee
              ) internal returns (uint256) {
                  uint256 totalFee = 0;
                  /* Take protocol fee if enabled. */
                  if (feeRate > 0 && protocolFee) {
                      uint256 fee = (price * feeRate) / INVERSE_BASIS_POINT;
                      _transferTo(paymentToken, from, feeRecipient, fee);
                      totalFee += fee;
                  }
                  /* Take order fees. */
                  for (uint8 i = 0; i < fees.length; i++) {
                      uint256 fee = (price * fees[i].rate) / INVERSE_BASIS_POINT;
                      _transferTo(paymentToken, from, fees[i].recipient, fee);
                      totalFee += fee;
                  }
                  require(totalFee <= price, "Fees are more than the price");
                  return totalFee;
              }
              /**
               * @dev Transfer amount in ETH or WETH
               * @param paymentToken address of token to pay in
               * @param from token sender
               * @param to token recipient
               * @param amount amount to transfer
               */
              function _transferTo(
                  address paymentToken,
                  address from,
                  address to,
                  uint256 amount
              ) internal {
                  if (amount == 0) {
                      return;
                  }
                  if (paymentToken == address(0)) {
                      /* Transfer funds in ETH. */
                      require(to != address(0), "Transfer to zero address");
                      (bool success,) = payable(to).call{value: amount}("");
                      require(success, "ETH transfer failed");
                  } else if (paymentToken == POOL) {
                      /* Transfer Pool funds. */
                      bool success = IBlurPool(POOL).transferFrom(from, to, amount);
                      require(success, "Pool transfer failed");
                  } else if (paymentToken == WETH) {
                      /* Transfer funds in WETH. */
                      executionDelegate.transferERC20(WETH, from, to, amount);
                  } else {
                      revert("Invalid payment token");
                  }
              }
              /**
               * @dev Execute call through delegate proxy
               * @param collection collection contract address
               * @param from seller address
               * @param to buyer address
               * @param tokenId tokenId
               * @param assetType asset type of the token
               */
              function _executeTokenTransfer(
                  address collection,
                  address from,
                  address to,
                  uint256 tokenId,
                  uint256 amount,
                  AssetType assetType
              ) internal {
                  /* Call execution delegate. */
                  if (assetType == AssetType.ERC721) {
                      executionDelegate.transferERC721(collection, from, to, tokenId);
                  } else if (assetType == AssetType.ERC1155) {
                      executionDelegate.transferERC1155(collection, from, to, tokenId, amount);
                  }
              }
              /**
               * @dev Return remaining ETH sent to bulkExecute or execute
               */
              function _returnDust() private {
                  uint256 _remainingETH = remainingETH;
                  assembly {
                      if gt(_remainingETH, 0) {
                          let callStatus := call(
                              gas(),
                              caller(),
                              _remainingETH,
                              0,
                              0,
                              0,
                              0
                          )
                          if iszero(callStatus) {
                            revert(0, 0)
                          }
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
          pragma solidity ^0.8.2;
          import "../../utils/AddressUpgradeable.sol";
          /**
           * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
           * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
           * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
           * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
           *
           * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
           * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
           * case an upgrade adds a module that needs to be initialized.
           *
           * For example:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * contract MyToken is ERC20Upgradeable {
           *     function initialize() initializer public {
           *         __ERC20_init("MyToken", "MTK");
           *     }
           * }
           * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
           *     function initializeV2() reinitializer(2) public {
           *         __ERC20Permit_init("MyToken");
           *     }
           * }
           * ```
           *
           * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
           * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
           *
           * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
           * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
           *
           * [CAUTION]
           * ====
           * Avoid leaving a contract uninitialized.
           *
           * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
           * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
           * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * /// @custom:oz-upgrades-unsafe-allow constructor
           * constructor() {
           *     _disableInitializers();
           * }
           * ```
           * ====
           */
          abstract contract Initializable {
              /**
               * @dev Indicates that the contract has been initialized.
               * @custom:oz-retyped-from bool
               */
              uint8 private _initialized;
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private _initializing;
              /**
               * @dev Triggered when the contract has been initialized or reinitialized.
               */
              event Initialized(uint8 version);
              /**
               * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
               * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
               */
              modifier initializer() {
                  bool isTopLevelCall = !_initializing;
                  require(
                      (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                      "Initializable: contract is already initialized"
                  );
                  _initialized = 1;
                  if (isTopLevelCall) {
                      _initializing = true;
                  }
                  _;
                  if (isTopLevelCall) {
                      _initializing = false;
                      emit Initialized(1);
                  }
              }
              /**
               * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
               * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
               * used to initialize parent contracts.
               *
               * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
               * initialization step. This is essential to configure modules that are added through upgrades and that require
               * initialization.
               *
               * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
               * a contract, executing them in the right order is up to the developer or operator.
               */
              modifier reinitializer(uint8 version) {
                  require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                  _initialized = version;
                  _initializing = true;
                  _;
                  _initializing = false;
                  emit Initialized(version);
              }
              /**
               * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
               * {initializer} and {reinitializer} modifiers, directly or indirectly.
               */
              modifier onlyInitializing() {
                  require(_initializing, "Initializable: contract is not initializing");
                  _;
              }
              /**
               * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
               * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
               * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
               * through proxies.
               */
              function _disableInitializers() internal virtual {
                  require(!_initializing, "Initializable: contract is initializing");
                  if (_initialized < type(uint8).max) {
                      _initialized = type(uint8).max;
                      emit Initialized(type(uint8).max);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/UUPSUpgradeable.sol)
          pragma solidity ^0.8.0;
          import "../../interfaces/draft-IERC1822Upgradeable.sol";
          import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
          import "./Initializable.sol";
          /**
           * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
           * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
           *
           * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
           * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
           * `UUPSUpgradeable` with a custom implementation of upgrades.
           *
           * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
           *
           * _Available since v4.1._
           */
          abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
              function __UUPSUpgradeable_init() internal onlyInitializing {
              }
              function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
              }
              /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
              address private immutable __self = address(this);
              /**
               * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
               * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
               * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
               * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
               * fail.
               */
              modifier onlyProxy() {
                  require(address(this) != __self, "Function must be called through delegatecall");
                  require(_getImplementation() == __self, "Function must be called through active proxy");
                  _;
              }
              /**
               * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
               * callable on the implementing contract but not through proxies.
               */
              modifier notDelegated() {
                  require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
                  _;
              }
              /**
               * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
               * implementation. It is used to validate that the this implementation remains valid after an upgrade.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
               */
              function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
                  return _IMPLEMENTATION_SLOT;
              }
              /**
               * @dev Upgrade the implementation of the proxy to `newImplementation`.
               *
               * Calls {_authorizeUpgrade}.
               *
               * Emits an {Upgraded} event.
               */
              function upgradeTo(address newImplementation) external virtual onlyProxy {
                  _authorizeUpgrade(newImplementation);
                  _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
              }
              /**
               * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
               * encoded in `data`.
               *
               * Calls {_authorizeUpgrade}.
               *
               * Emits an {Upgraded} event.
               */
              function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
                  _authorizeUpgrade(newImplementation);
                  _upgradeToAndCallUUPS(newImplementation, data, true);
              }
              /**
               * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
               * {upgradeTo} and {upgradeToAndCall}.
               *
               * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
               *
               * ```solidity
               * function _authorizeUpgrade(address) internal override onlyOwner {}
               * ```
               */
              function _authorizeUpgrade(address newImplementation) internal virtual;
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
          pragma solidity ^0.8.0;
          import "../utils/ContextUpgradeable.sol";
          import "../proxy/utils/Initializable.sol";
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
              address private _owner;
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              function __Ownable_init() internal onlyInitializing {
                  __Ownable_init_unchained();
              }
              function __Ownable_init_unchained() internal onlyInitializing {
                  _transferOwnership(_msgSender());
              }
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  _checkOwner();
                  _;
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if the sender is not the owner.
               */
              function _checkOwner() internal view virtual {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
              }
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          /**
           * @title ReentrancyGuarded
           * @dev Protections for reentrancy attacks
           */
          contract ReentrancyGuarded {
              bool private reentrancyLock = false;
              /* Prevent a contract function from being reentrant-called. */
              modifier reentrancyGuard {
                  require(!reentrancyLock, "Reentrancy detected");
                  reentrancyLock = true;
                  _;
                  reentrancyLock = false;
              }
              uint256[49] private __gap;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {Order, Fee} from "./OrderStructs.sol";
          /**
           * @title EIP712
           * @dev Contains all of the order hashing functions for EIP712 compliant signatures
           */
          contract EIP712 {
              struct EIP712Domain {
                  string  name;
                  string  version;
                  uint256 chainId;
                  address verifyingContract;
              }
              /* Order typehash for EIP 712 compatibility. */
              bytes32 constant public FEE_TYPEHASH = keccak256(
                  "Fee(uint16 rate,address recipient)"
              );
              bytes32 constant public ORDER_TYPEHASH = keccak256(
                  "Order(address trader,uint8 side,address matchingPolicy,address collection,uint256 tokenId,uint256 amount,address paymentToken,uint256 price,uint256 listingTime,uint256 expirationTime,Fee[] fees,uint256 salt,bytes extraParams,uint256 nonce)Fee(uint16 rate,address recipient)"
              );
              bytes32 constant public ORACLE_ORDER_TYPEHASH = keccak256(
                  "OracleOrder(Order order,uint256 blockNumber)Fee(uint16 rate,address recipient)Order(address trader,uint8 side,address matchingPolicy,address collection,uint256 tokenId,uint256 amount,address paymentToken,uint256 price,uint256 listingTime,uint256 expirationTime,Fee[] fees,uint256 salt,bytes extraParams,uint256 nonce)"
              );
              bytes32 constant public ROOT_TYPEHASH = keccak256(
                  "Root(bytes32 root)"
              );
              bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256(
                  "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
              );
              bytes32 DOMAIN_SEPARATOR;
              function _hashDomain(EIP712Domain memory eip712Domain)
                  internal
                  pure
                  returns (bytes32)
              {
                  return keccak256(
                      abi.encode(
                          EIP712DOMAIN_TYPEHASH,
                          keccak256(bytes(eip712Domain.name)),
                          keccak256(bytes(eip712Domain.version)),
                          eip712Domain.chainId,
                          eip712Domain.verifyingContract
                      )
                  );
              }
              function _hashFee(Fee calldata fee)
                  internal 
                  pure
                  returns (bytes32)
              {
                  return keccak256(
                      abi.encode(
                          FEE_TYPEHASH,
                          fee.rate,
                          fee.recipient
                      )
                  );
              }
              function _packFees(Fee[] calldata fees)
                  internal
                  pure
                  returns (bytes32)
              {
                  bytes32[] memory feeHashes = new bytes32[](
                      fees.length
                  );
                  for (uint256 i = 0; i < fees.length; i++) {
                      feeHashes[i] = _hashFee(fees[i]);
                  }
                  return keccak256(abi.encodePacked(feeHashes));
              }
              function _hashOrder(Order calldata order, uint256 nonce)
                  internal
                  pure
                  returns (bytes32)
              {
                  return keccak256(
                      bytes.concat(
                          abi.encode(
                                ORDER_TYPEHASH,
                                order.trader,
                                order.side,
                                order.matchingPolicy,
                                order.collection,
                                order.tokenId,
                                order.amount,
                                order.paymentToken,
                                order.price,
                                order.listingTime,
                                order.expirationTime,
                                _packFees(order.fees),
                                order.salt,
                                keccak256(order.extraParams)
                          ),
                          abi.encode(nonce)
                      )
                  );
              }
              function _hashToSign(bytes32 orderHash)
                  internal
                  view
                  returns (bytes32 hash)
              {
                  return keccak256(abi.encodePacked(
                      "\\x19\\x01",
                      DOMAIN_SEPARATOR,
                      orderHash
                  ));
              }
              function _hashToSignRoot(bytes32 root)
                  internal
                  view
                  returns (bytes32 hash)
              {
                  return keccak256(abi.encodePacked(
                      "\\x19\\x01",
                      DOMAIN_SEPARATOR,
                      keccak256(abi.encode(
                          ROOT_TYPEHASH,
                          root
                      ))
                  ));
              }
              function _hashToSignOracle(bytes32 orderHash, uint256 blockNumber)
                  internal
                  view
                  returns (bytes32 hash)
              {
                  return keccak256(abi.encodePacked(
                      "\\x19\\x01",
                      DOMAIN_SEPARATOR,
                      keccak256(abi.encode(
                          ORACLE_ORDER_TYPEHASH,
                          orderHash,
                          blockNumber
                      ))
                  ));
              }
              uint256[44] private __gap;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          /**
           * @title MerkleVerifier
           * @dev Utility functions for Merkle tree computations
           */
          library MerkleVerifier {
              error InvalidProof();
              /**
               * @dev Verify the merkle proof
               * @param leaf leaf
               * @param root root
               * @param proof proof
               */
              function _verifyProof(
                  bytes32 leaf,
                  bytes32 root,
                  bytes32[] memory proof
              ) public pure {
                  bytes32 computedRoot = _computeRoot(leaf, proof);
                  if (computedRoot != root) {
                      revert InvalidProof();
                  }
              }
              /**
               * @dev Compute the merkle root
               * @param leaf leaf
               * @param proof proof
               */
              function _computeRoot(
                  bytes32 leaf,
                  bytes32[] memory proof
              ) public pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      bytes32 proofElement = proof[i];
                      computedHash = _hashPair(computedHash, proofElement);
                  }
                  return computedHash;
              }
              function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
                  return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
              }
              function _efficientHash(
                  bytes32 a,
                  bytes32 b
              ) private pure returns (bytes32 value) {
                  assembly {
                      mstore(0x00, a)
                      mstore(0x20, b)
                      value := keccak256(0x00, 0x40)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {Input, Order} from "../lib/OrderStructs.sol";
          import "./IExecutionDelegate.sol";
          import "./IPolicyManager.sol";
          interface IBlurExchange {
              function nonces(address) external view returns (uint256);
              function close() external;
              function initialize(
                  IExecutionDelegate _executionDelegate,
                  IPolicyManager _policyManager,
                  address _oracle,
                  uint _blockRange
              ) external;
              function setExecutionDelegate(IExecutionDelegate _executionDelegate) external;
              function setPolicyManager(IPolicyManager _policyManager) external;
              function setOracle(address _oracle) external;
              function setBlockRange(uint256 _blockRange) external;
              function cancelOrder(Order calldata order) external;
              function cancelOrders(Order[] calldata orders) external;
              function incrementNonce() external;
              function execute(Input calldata sell, Input calldata buy)
                  external
                  payable;
          }
          pragma solidity ^0.8.17;
          interface IBlurPool {
              event Transfer(address indexed from, address indexed to, uint256 amount);
              function totalSupply() external view returns (uint256);
              function balanceOf(address user) external view returns (uint256);
              function deposit() external payable;
              function withdraw(uint256) external;
              function transferFrom(address from, address to, uint256 amount)
                  external
                  returns (bool);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          interface IExecutionDelegate {
              function approveContract(address _contract) external;
              function denyContract(address _contract) external;
              function revokeApproval() external;
              function grantApproval() external;
              function transferERC721Unsafe(address collection, address from, address to, uint256 tokenId) external;
              function transferERC721(address collection, address from, address to, uint256 tokenId) external;
              function transferERC1155(address collection, address from, address to, uint256 tokenId, uint256 amount) external;
              function transferERC20(address token, address from, address to, uint256 amount) external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          interface IPolicyManager {
              function addPolicy(address policy) external;
              function removePolicy(address policy) external;
              function isPolicyWhitelisted(address policy) external view returns (bool);
              function viewWhitelistedPolicies(uint256 cursor, uint256 size) external view returns (address[] memory, uint256);
              function viewCountWhitelistedPolicies() external view returns (uint256);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {Order, AssetType} from "../lib/OrderStructs.sol";
          interface IMatchingPolicy {
              function canMatchMakerAsk(Order calldata makerAsk, Order calldata takerBid)
                  external
                  view
                  returns (
                      bool,
                      uint256,
                      uint256,
                      uint256,
                      AssetType
                  );
              function canMatchMakerBid(Order calldata makerBid, Order calldata takerAsk)
                  external
                  view
                  returns (
                      bool,
                      uint256,
                      uint256,
                      uint256,
                      AssetType
                  );
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          enum Side { Buy, Sell }
          enum SignatureVersion { Single, Bulk }
          enum AssetType { ERC721, ERC1155 }
          struct Fee {
              uint16 rate;
              address payable recipient;
          }
          struct Order {
              address trader;
              Side side;
              address matchingPolicy;
              address collection;
              uint256 tokenId;
              uint256 amount;
              address paymentToken;
              uint256 price;
              uint256 listingTime;
              /* Order expiration timestamp - 0 for oracle cancellations. */
              uint256 expirationTime;
              Fee[] fees;
              uint256 salt;
              bytes extraParams;
          }
          struct Input {
              Order order;
              uint8 v;
              bytes32 r;
              bytes32 s;
              bytes extraSignature;
              SignatureVersion signatureVersion;
              uint256 blockNumber;
          }
          struct Execution {
            Input sell;
            Input buy;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
          pragma solidity ^0.8.1;
          /**
           * @dev Collection of functions related to the address type
           */
          library AddressUpgradeable {
              /**
               * @dev Returns true if `account` is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, `isContract` will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               *
               * [IMPORTANT]
               * ====
               * You shouldn't rely on `isContract` to protect against flash loan attacks!
               *
               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
               * constructor.
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize/address.code.length, which returns 0
                  // for contracts in construction, since the code is only stored at the end
                  // of the constructor execution.
                  return account.code.length > 0;
              }
              /**
               * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
               * `recipient`, forwarding all available gas and reverting on errors.
               *
               * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
               * of certain opcodes, possibly making contracts go over the 2300 gas limit
               * imposed by `transfer`, making them unable to receive funds via
               * `transfer`. {sendValue} removes this limitation.
               *
               * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
               *
               * IMPORTANT: because control is transferred to `recipient`, care must be
               * taken to not create reentrancy vulnerabilities. Consider using
               * {ReentrancyGuard} or the
               * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
               */
              function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, "Address: insufficient balance");
                  (bool success, ) = recipient.call{value: amount}("");
                  require(success, "Address: unable to send value, recipient may have reverted");
              }
              /**
               * @dev Performs a Solidity function call using a low level `call`. A
               * plain `call` is an unsafe replacement for a function call: use this
               * function instead.
               *
               * If `target` reverts with a revert reason, it is bubbled up by this
               * function (like regular Solidity function calls).
               *
               * Returns the raw returned data. To convert to the expected return value,
               * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
               *
               * Requirements:
               *
               * - `target` must be a contract.
               * - calling `target` with `data` must not revert.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCall(target, data, "Address: low-level call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
               * `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but also transferring `value` wei to `target`.
               *
               * Requirements:
               *
               * - the calling contract must have an ETH balance of at least `value`.
               * - the called Solidity function must be `payable`.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(
                  address target,
                  bytes memory data,
                  uint256 value
              ) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
              }
              /**
               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
               * with `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(
                  address target,
                  bytes memory data,
                  uint256 value,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  require(address(this).balance >= value, "Address: insufficient balance for call");
                  require(isContract(target), "Address: call to non-contract");
                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                  return verifyCallResult(success, returndata, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                  return functionStaticCall(target, data, "Address: low-level static call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal view returns (bytes memory) {
                  require(isContract(target), "Address: static call to non-contract");
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return verifyCallResult(success, returndata, errorMessage);
              }
              /**
               * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
               * revert reason using the provided one.
               *
               * _Available since v4.3._
               */
              function verifyCallResult(
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal pure returns (bytes memory) {
                  if (success) {
                      return returndata;
                  } else {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
           * proxy whose upgrades are fully controlled by the current implementation.
           */
          interface IERC1822ProxiableUpgradeable {
              /**
               * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
               * address.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy.
               */
              function proxiableUUID() external view returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
          pragma solidity ^0.8.2;
          import "../beacon/IBeaconUpgradeable.sol";
          import "../../interfaces/draft-IERC1822Upgradeable.sol";
          import "../../utils/AddressUpgradeable.sol";
          import "../../utils/StorageSlotUpgradeable.sol";
          import "../utils/Initializable.sol";
          /**
           * @dev This abstract contract provides getters and event emitting update functions for
           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
           *
           * _Available since v4.1._
           *
           * @custom:oz-upgrades-unsafe-allow delegatecall
           */
          abstract contract ERC1967UpgradeUpgradeable is Initializable {
              function __ERC1967Upgrade_init() internal onlyInitializing {
              }
              function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
              }
              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
              /**
               * @dev Storage slot with the address of the current implementation.
               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
              /**
               * @dev Emitted when the implementation is upgraded.
               */
              event Upgraded(address indexed implementation);
              /**
               * @dev Returns the current implementation address.
               */
              function _getImplementation() internal view returns (address) {
                  return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
              }
              /**
               * @dev Stores a new address in the EIP1967 implementation slot.
               */
              function _setImplementation(address newImplementation) private {
                  require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                  StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
              }
              /**
               * @dev Perform implementation upgrade
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeTo(address newImplementation) internal {
                  _setImplementation(newImplementation);
                  emit Upgraded(newImplementation);
              }
              /**
               * @dev Perform implementation upgrade with additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCall(
                  address newImplementation,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  _upgradeTo(newImplementation);
                  if (data.length > 0 || forceCall) {
                      _functionDelegateCall(newImplementation, data);
                  }
              }
              /**
               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCallUUPS(
                  address newImplementation,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                  if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
                      _setImplementation(newImplementation);
                  } else {
                      try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                      } catch {
                          revert("ERC1967Upgrade: new implementation is not UUPS");
                      }
                      _upgradeToAndCall(newImplementation, data, forceCall);
                  }
              }
              /**
               * @dev Storage slot with the admin of the contract.
               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
              /**
               * @dev Emitted when the admin account has changed.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
              /**
               * @dev Returns the current admin.
               */
              function _getAdmin() internal view returns (address) {
                  return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
              }
              /**
               * @dev Stores a new address in the EIP1967 admin slot.
               */
              function _setAdmin(address newAdmin) private {
                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                  StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
              }
              /**
               * @dev Changes the admin of the proxy.
               *
               * Emits an {AdminChanged} event.
               */
              function _changeAdmin(address newAdmin) internal {
                  emit AdminChanged(_getAdmin(), newAdmin);
                  _setAdmin(newAdmin);
              }
              /**
               * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
               * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
               */
              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
              /**
               * @dev Emitted when the beacon is upgraded.
               */
              event BeaconUpgraded(address indexed beacon);
              /**
               * @dev Returns the current beacon.
               */
              function _getBeacon() internal view returns (address) {
                  return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
              }
              /**
               * @dev Stores a new beacon in the EIP1967 beacon slot.
               */
              function _setBeacon(address newBeacon) private {
                  require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                  require(
                      AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
                      "ERC1967: beacon implementation is not a contract"
                  );
                  StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
              }
              /**
               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
               *
               * Emits a {BeaconUpgraded} event.
               */
              function _upgradeBeaconToAndCall(
                  address newBeacon,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  _setBeacon(newBeacon);
                  emit BeaconUpgraded(newBeacon);
                  if (data.length > 0 || forceCall) {
                      _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
                  }
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
               * but performing a delegate call.
               *
               * _Available since v3.4._
               */
              function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
                  require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = target.delegatecall(data);
                  return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev This is the interface that {BeaconProxy} expects of its beacon.
           */
          interface IBeaconUpgradeable {
              /**
               * @dev Must return an address that can be used as a delegate call target.
               *
               * {BeaconProxy} will check that this address is a contract.
               */
              function implementation() external view returns (address);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Library for reading and writing primitive types to specific storage slots.
           *
           * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
           * This library helps with reading and writing to such slots without the need for inline assembly.
           *
           * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
           *
           * Example usage to set ERC1967 implementation slot:
           * ```
           * contract ERC1967 {
           *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
           *
           *     function _getImplementation() internal view returns (address) {
           *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
           *     }
           *
           *     function _setImplementation(address newImplementation) internal {
           *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
           *     }
           * }
           * ```
           *
           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
           */
          library StorageSlotUpgradeable {
              struct AddressSlot {
                  address value;
              }
              struct BooleanSlot {
                  bool value;
              }
              struct Bytes32Slot {
                  bytes32 value;
              }
              struct Uint256Slot {
                  uint256 value;
              }
              /**
               * @dev Returns an `AddressSlot` with member `value` located at `slot`.
               */
              function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
               */
              function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
               */
              function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
               */
              function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          import "../proxy/utils/Initializable.sol";
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract ContextUpgradeable is Initializable {
              function __Context_init() internal onlyInitializing {
              }
              function __Context_init_unchained() internal onlyInitializing {
              }
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          

          File 3 of 4: StandardPolicyERC721
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {Order, AssetType} from "../lib/OrderStructs.sol";
          import {IMatchingPolicy} from "../interfaces/IMatchingPolicy.sol";
          /**
           * @title StandardPolicyERC721
           * @dev Policy for matching orders at a fixed price for a specific ERC721 tokenId (requires oracle authorization on both orders)
           */
          contract StandardPolicyERC721 is IMatchingPolicy {
              function canMatchMakerAsk(Order calldata makerAsk, Order calldata takerBid)
                  external
                  pure
                  override
                  returns (
                      bool,
                      uint256,
                      uint256,
                      uint256,
                      AssetType
                  )
              {
                  return (
                      (makerAsk.side != takerBid.side) &&
                      (makerAsk.paymentToken == takerBid.paymentToken) &&
                      (makerAsk.collection == takerBid.collection) &&
                      (makerAsk.tokenId == takerBid.tokenId) &&
                      (makerAsk.extraParams.length > 0 && makerAsk.extraParams[0] == "\\x01") &&
                      (takerBid.extraParams.length > 0 && takerBid.extraParams[0] == "\\x01") &&
                      (makerAsk.amount == 1) &&
                      (takerBid.amount == 1) &&
                      (makerAsk.matchingPolicy == takerBid.matchingPolicy) &&
                      (makerAsk.price == takerBid.price),
                      makerAsk.price,
                      makerAsk.tokenId,
                      1,
                      AssetType.ERC721
                  );
              }
              function canMatchMakerBid(Order calldata makerBid, Order calldata takerAsk)
                  external
                  pure
                  override
                  returns (
                      bool,
                      uint256,
                      uint256,
                      uint256,
                      AssetType
                  )
              {
                  return (
                      (makerBid.side != takerAsk.side) &&
                      (makerBid.paymentToken == takerAsk.paymentToken) &&
                      (makerBid.collection == takerAsk.collection) &&
                      (makerBid.tokenId == takerAsk.tokenId) &&
                      (makerBid.extraParams.length > 0 && makerBid.extraParams[0] == "\\x01") &&
                      (takerAsk.extraParams.length > 0 && takerAsk.extraParams[0] == "\\x01") &&
                      (makerBid.amount == 1) &&
                      (takerAsk.amount == 1) &&
                      (makerBid.matchingPolicy == takerAsk.matchingPolicy) &&
                      (makerBid.price == takerAsk.price),
                      makerBid.price,
                      makerBid.tokenId,
                      1,
                      AssetType.ERC721
                  );
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          enum Side { Buy, Sell }
          enum SignatureVersion { Single, Bulk }
          enum AssetType { ERC721, ERC1155 }
          struct Fee {
              uint16 rate;
              address payable recipient;
          }
          struct Order {
              address trader;
              Side side;
              address matchingPolicy;
              address collection;
              uint256 tokenId;
              uint256 amount;
              address paymentToken;
              uint256 price;
              uint256 listingTime;
              /* Order expiration timestamp - 0 for oracle cancellations. */
              uint256 expirationTime;
              Fee[] fees;
              uint256 salt;
              bytes extraParams;
          }
          struct Input {
              Order order;
              uint8 v;
              bytes32 r;
              bytes32 s;
              bytes extraSignature;
              SignatureVersion signatureVersion;
              uint256 blockNumber;
          }
          struct Execution {
            Input sell;
            Input buy;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {Order, AssetType} from "../lib/OrderStructs.sol";
          interface IMatchingPolicy {
              function canMatchMakerAsk(Order calldata makerAsk, Order calldata takerBid)
                  external
                  view
                  returns (
                      bool,
                      uint256,
                      uint256,
                      uint256,
                      AssetType
                  );
              function canMatchMakerBid(Order calldata makerBid, Order calldata takerAsk)
                  external
                  view
                  returns (
                      bool,
                      uint256,
                      uint256,
                      uint256,
                      AssetType
                  );
          }
          

          File 4 of 4: OperatorFilterRegistry
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
          pragma solidity ^0.8.0;
          import "../utils/Context.sol";
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract Ownable is Context {
              address private _owner;
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              constructor() {
                  _transferOwnership(_msgSender());
              }
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  _checkOwner();
                  _;
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if the sender is not the owner.
               */
              function _checkOwner() internal view virtual {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
              }
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)
          // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
          pragma solidity ^0.8.0;
          /**
           * @dev Library for managing
           * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
           * types.
           *
           * Sets have the following properties:
           *
           * - Elements are added, removed, and checked for existence in constant time
           * (O(1)).
           * - Elements are enumerated in O(n). No guarantees are made on the ordering.
           *
           * ```
           * contract Example {
           *     // Add the library methods
           *     using EnumerableSet for EnumerableSet.AddressSet;
           *
           *     // Declare a set state variable
           *     EnumerableSet.AddressSet private mySet;
           * }
           * ```
           *
           * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
           * and `uint256` (`UintSet`) are supported.
           *
           * [WARNING]
           * ====
           * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
           * unusable.
           * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
           *
           * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
           * array of EnumerableSet.
           * ====
           */
          library EnumerableSet {
              // To implement this library for multiple types with as little code
              // repetition as possible, we write it in terms of a generic Set type with
              // bytes32 values.
              // The Set implementation uses private functions, and user-facing
              // implementations (such as AddressSet) are just wrappers around the
              // underlying Set.
              // This means that we can only create new EnumerableSets for types that fit
              // in bytes32.
              struct Set {
                  // Storage of set values
                  bytes32[] _values;
                  // Position of the value in the `values` array, plus 1 because index 0
                  // means a value is not in the set.
                  mapping(bytes32 => uint256) _indexes;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function _add(Set storage set, bytes32 value) private returns (bool) {
                  if (!_contains(set, value)) {
                      set._values.push(value);
                      // The value is stored at length-1, but we add 1 to all indexes
                      // and use 0 as a sentinel value
                      set._indexes[value] = set._values.length;
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function _remove(Set storage set, bytes32 value) private returns (bool) {
                  // We read and store the value's index to prevent multiple reads from the same storage slot
                  uint256 valueIndex = set._indexes[value];
                  if (valueIndex != 0) {
                      // Equivalent to contains(set, value)
                      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                      // the array, and then remove the last element (sometimes called as 'swap and pop').
                      // This modifies the order of the array, as noted in {at}.
                      uint256 toDeleteIndex = valueIndex - 1;
                      uint256 lastIndex = set._values.length - 1;
                      if (lastIndex != toDeleteIndex) {
                          bytes32 lastValue = set._values[lastIndex];
                          // Move the last value to the index where the value to delete is
                          set._values[toDeleteIndex] = lastValue;
                          // Update the index for the moved value
                          set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                      }
                      // Delete the slot where the moved value was stored
                      set._values.pop();
                      // Delete the index for the deleted slot
                      delete set._indexes[value];
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function _contains(Set storage set, bytes32 value) private view returns (bool) {
                  return set._indexes[value] != 0;
              }
              /**
               * @dev Returns the number of values on the set. O(1).
               */
              function _length(Set storage set) private view returns (uint256) {
                  return set._values.length;
              }
              /**
               * @dev Returns the value stored at position `index` in the set. O(1).
               *
               * Note that there are no guarantees on the ordering of values inside the
               * array, and it may change when more values are added or removed.
               *
               * Requirements:
               *
               * - `index` must be strictly less than {length}.
               */
              function _at(Set storage set, uint256 index) private view returns (bytes32) {
                  return set._values[index];
              }
              /**
               * @dev Return the entire set in an array
               *
               * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
               * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
               * this function has an unbounded cost, and using it as part of a state-changing function may render the function
               * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
               */
              function _values(Set storage set) private view returns (bytes32[] memory) {
                  return set._values;
              }
              // Bytes32Set
              struct Bytes32Set {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                  return _add(set._inner, value);
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                  return _remove(set._inner, value);
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
                  return _contains(set._inner, value);
              }
              /**
               * @dev Returns the number of values in the set. O(1).
               */
              function length(Bytes32Set storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
              /**
               * @dev Returns the value stored at position `index` in the set. O(1).
               *
               * Note that there are no guarantees on the ordering of values inside the
               * array, and it may change when more values are added or removed.
               *
               * Requirements:
               *
               * - `index` must be strictly less than {length}.
               */
              function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
                  return _at(set._inner, index);
              }
              /**
               * @dev Return the entire set in an array
               *
               * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
               * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
               * this function has an unbounded cost, and using it as part of a state-changing function may render the function
               * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
               */
              function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
                  bytes32[] memory store = _values(set._inner);
                  bytes32[] memory result;
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := store
                  }
                  return result;
              }
              // AddressSet
              struct AddressSet {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(AddressSet storage set, address value) internal returns (bool) {
                  return _add(set._inner, bytes32(uint256(uint160(value))));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(AddressSet storage set, address value) internal returns (bool) {
                  return _remove(set._inner, bytes32(uint256(uint160(value))));
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(AddressSet storage set, address value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(uint256(uint160(value))));
              }
              /**
               * @dev Returns the number of values in the set. O(1).
               */
              function length(AddressSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
              /**
               * @dev Returns the value stored at position `index` in the set. O(1).
               *
               * Note that there are no guarantees on the ordering of values inside the
               * array, and it may change when more values are added or removed.
               *
               * Requirements:
               *
               * - `index` must be strictly less than {length}.
               */
              function at(AddressSet storage set, uint256 index) internal view returns (address) {
                  return address(uint160(uint256(_at(set._inner, index))));
              }
              /**
               * @dev Return the entire set in an array
               *
               * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
               * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
               * this function has an unbounded cost, and using it as part of a state-changing function may render the function
               * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
               */
              function values(AddressSet storage set) internal view returns (address[] memory) {
                  bytes32[] memory store = _values(set._inner);
                  address[] memory result;
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := store
                  }
                  return result;
              }
              // UintSet
              struct UintSet {
                  Set _inner;
              }
              /**
               * @dev Add a value to a set. O(1).
               *
               * Returns true if the value was added to the set, that is if it was not
               * already present.
               */
              function add(UintSet storage set, uint256 value) internal returns (bool) {
                  return _add(set._inner, bytes32(value));
              }
              /**
               * @dev Removes a value from a set. O(1).
               *
               * Returns true if the value was removed from the set, that is if it was
               * present.
               */
              function remove(UintSet storage set, uint256 value) internal returns (bool) {
                  return _remove(set._inner, bytes32(value));
              }
              /**
               * @dev Returns true if the value is in the set. O(1).
               */
              function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                  return _contains(set._inner, bytes32(value));
              }
              /**
               * @dev Returns the number of values in the set. O(1).
               */
              function length(UintSet storage set) internal view returns (uint256) {
                  return _length(set._inner);
              }
              /**
               * @dev Returns the value stored at position `index` in the set. O(1).
               *
               * Note that there are no guarantees on the ordering of values inside the
               * array, and it may change when more values are added or removed.
               *
               * Requirements:
               *
               * - `index` must be strictly less than {length}.
               */
              function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                  return uint256(_at(set._inner, index));
              }
              /**
               * @dev Return the entire set in an array
               *
               * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
               * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
               * this function has an unbounded cost, and using it as part of a state-changing function may render the function
               * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
               */
              function values(UintSet storage set) internal view returns (uint256[] memory) {
                  bytes32[] memory store = _values(set._inner);
                  uint256[] memory result;
                  /// @solidity memory-safe-assembly
                  assembly {
                      result := store
                  }
                  return result;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol";
          interface IOperatorFilterRegistry {
              function isOperatorAllowed(address registrant, address operator) external returns (bool);
              function register(address registrant) external;
              function registerAndSubscribe(address registrant, address subscription) external;
              function registerAndCopyEntries(address registrant, address registrantToCopy) external;
              function updateOperator(address registrant, address operator, bool filtered) external;
              function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
              function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
              function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
              function subscribe(address registrant, address registrantToSubscribe) external;
              function unsubscribe(address registrant, bool copyExistingEntries) external;
              function subscriptionOf(address addr) external returns (address registrant);
              function subscribers(address registrant) external returns (address[] memory);
              function subscriberAt(address registrant, uint256 index) external returns (address);
              function copyEntriesOf(address registrant, address registrantToCopy) external;
              function isOperatorFiltered(address registrant, address operator) external returns (bool);
              function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
              function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
              function filteredOperators(address addr) external returns (address[] memory);
              function filteredCodeHashes(address addr) external returns (bytes32[] memory);
              function filteredOperatorAt(address registrant, uint256 index) external returns (address);
              function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
              function isRegistered(address addr) external returns (bool);
              function codeHashOf(address addr) external returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
          import {Ownable} from "openzeppelin-contracts/access/Ownable.sol";
          import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol";
          import {OperatorFilterRegistryErrorsAndEvents} from "./OperatorFilterRegistryErrorsAndEvents.sol";
          /**
           * @title  OperatorFilterRegistry
           * @notice Borrows heavily from the QQL BlacklistOperatorFilter contract:
           *         https://github.com/qql-art/contracts/blob/main/contracts/BlacklistOperatorFilter.sol
           * @notice This contracts allows tokens or token owners to register specific addresses or codeHashes that may be
           * *       restricted according to the isOperatorAllowed function.
           */
          contract OperatorFilterRegistry is IOperatorFilterRegistry, OperatorFilterRegistryErrorsAndEvents {
              using EnumerableSet for EnumerableSet.AddressSet;
              using EnumerableSet for EnumerableSet.Bytes32Set;
              /// @dev initialized accounts have a nonzero codehash (see https://eips.ethereum.org/EIPS/eip-1052)
              /// Note that this will also be a smart contract's codehash when making calls from its constructor.
              bytes32 constant EOA_CODEHASH = keccak256("");
              mapping(address => EnumerableSet.AddressSet) private _filteredOperators;
              mapping(address => EnumerableSet.Bytes32Set) private _filteredCodeHashes;
              mapping(address => address) private _registrations;
              mapping(address => EnumerableSet.AddressSet) private _subscribers;
              /**
               * @notice restricts method caller to the address or EIP-173 "owner()"
               */
              modifier onlyAddressOrOwner(address addr) {
                  if (msg.sender != addr) {
                      try Ownable(addr).owner() returns (address owner) {
                          if (msg.sender != owner) {
                              revert OnlyAddressOrOwner();
                          }
                      } catch (bytes memory reason) {
                          if (reason.length == 0) {
                              revert NotOwnable();
                          } else {
                              /// @solidity memory-safe-assembly
                              assembly {
                                  revert(add(32, reason), mload(reason))
                              }
                          }
                      }
                  }
                  _;
              }
              /**
               * @notice Returns true if operator is not filtered for a given token, either by address or codeHash. Also returns
               *         true if supplied registrant address is not registered.
               */
              function isOperatorAllowed(address registrant, address operator) external view returns (bool) {
                  address registration = _registrations[registrant];
                  if (registration != address(0)) {
                      EnumerableSet.AddressSet storage filteredOperatorsRef;
                      EnumerableSet.Bytes32Set storage filteredCodeHashesRef;
                      filteredOperatorsRef = _filteredOperators[registration];
                      filteredCodeHashesRef = _filteredCodeHashes[registration];
                      if (filteredOperatorsRef.contains(operator)) {
                          revert AddressFiltered(operator);
                      }
                      if (operator.code.length > 0) {
                          bytes32 codeHash = operator.codehash;
                          if (filteredCodeHashesRef.contains(codeHash)) {
                              revert CodeHashFiltered(operator, codeHash);
                          }
                      }
                  }
                  return true;
              }
              //////////////////
              // AUTH METHODS //
              //////////////////
              /**
               * @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
               */
              function register(address registrant) external onlyAddressOrOwner(registrant) {
                  if (_registrations[registrant] != address(0)) {
                      revert AlreadyRegistered();
                  }
                  _registrations[registrant] = registrant;
                  emit RegistrationUpdated(registrant, true);
              }
              /**
               * @notice Unregisters an address with the registry and removes its subscription. May be called by address itself or by EIP-173 owner.
               *         Note that this does not remove any filtered addresses or codeHashes.
               *         Also note that any subscriptions to this registrant will still be active and follow the existing filtered addresses and codehashes.
               */
              function unregister(address registrant) external onlyAddressOrOwner(registrant) {
                  address registration = _registrations[registrant];
                  if (registration == address(0)) {
                      revert NotRegistered(registrant);
                  }
                  if (registration != registrant) {
                      _subscribers[registration].remove(registrant);
                      emit SubscriptionUpdated(registrant, registration, false);
                  }
                  _registrations[registrant] = address(0);
                  emit RegistrationUpdated(registrant, false);
              }
              /**
               * @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
               */
              function registerAndSubscribe(address registrant, address subscription) external onlyAddressOrOwner(registrant) {
                  address registration = _registrations[registrant];
                  if (registration != address(0)) {
                      revert AlreadyRegistered();
                  }
                  if (registrant == subscription) {
                      revert CannotSubscribeToSelf();
                  }
                  address subscriptionRegistration = _registrations[subscription];
                  if (subscriptionRegistration == address(0)) {
                      revert NotRegistered(subscription);
                  }
                  if (subscriptionRegistration != subscription) {
                      revert CannotSubscribeToRegistrantWithSubscription(subscription);
                  }
                  _registrations[registrant] = subscription;
                  _subscribers[subscription].add(registrant);
                  emit RegistrationUpdated(registrant, true);
                  emit SubscriptionUpdated(registrant, subscription, true);
              }
              /**
               * @notice Registers an address with the registry and copies the filtered operators and codeHashes from another
               *         address without subscribing.
               */
              function registerAndCopyEntries(address registrant, address registrantToCopy)
                  external
                  onlyAddressOrOwner(registrant)
              {
                  if (registrantToCopy == registrant) {
                      revert CannotCopyFromSelf();
                  }
                  address registration = _registrations[registrant];
                  if (registration != address(0)) {
                      revert AlreadyRegistered();
                  }
                  address registrantRegistration = _registrations[registrantToCopy];
                  if (registrantRegistration == address(0)) {
                      revert NotRegistered(registrantToCopy);
                  }
                  _registrations[registrant] = registrant;
                  emit RegistrationUpdated(registrant, true);
                  _copyEntries(registrant, registrantToCopy);
              }
              /**
               * @notice Update an operator address for a registered address - when filtered is true, the operator is filtered.
               */
              function updateOperator(address registrant, address operator, bool filtered)
                  external
                  onlyAddressOrOwner(registrant)
              {
                  address registration = _registrations[registrant];
                  if (registration == address(0)) {
                      revert NotRegistered(registrant);
                  }
                  if (registration != registrant) {
                      revert CannotUpdateWhileSubscribed(registration);
                  }
                  EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrant];
                  if (!filtered) {
                      bool removed = filteredOperatorsRef.remove(operator);
                      if (!removed) {
                          revert AddressNotFiltered(operator);
                      }
                  } else {
                      bool added = filteredOperatorsRef.add(operator);
                      if (!added) {
                          revert AddressAlreadyFiltered(operator);
                      }
                  }
                  emit OperatorUpdated(registrant, operator, filtered);
              }
              /**
               * @notice Update a codeHash for a registered address - when filtered is true, the codeHash is filtered.
               */
              function updateCodeHash(address registrant, bytes32 codeHash, bool filtered)
                  external
                  onlyAddressOrOwner(registrant)
              {
                  if (codeHash == EOA_CODEHASH) {
                      revert CannotFilterEOAs();
                  }
                  address registration = _registrations[registrant];
                  if (registration == address(0)) {
                      revert NotRegistered(registrant);
                  }
                  if (registration != registrant) {
                      revert CannotUpdateWhileSubscribed(registration);
                  }
                  EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrant];
                  if (!filtered) {
                      bool removed = filteredCodeHashesRef.remove(codeHash);
                      if (!removed) {
                          revert CodeHashNotFiltered(codeHash);
                      }
                  } else {
                      bool added = filteredCodeHashesRef.add(codeHash);
                      if (!added) {
                          revert CodeHashAlreadyFiltered(codeHash);
                      }
                  }
                  emit CodeHashUpdated(registrant, codeHash, filtered);
              }
              /**
               * @notice Update multiple operators for a registered address - when filtered is true, the operators will be filtered. Reverts on duplicates.
               */
              function updateOperators(address registrant, address[] calldata operators, bool filtered)
                  external
                  onlyAddressOrOwner(registrant)
              {
                  address registration = _registrations[registrant];
                  if (registration == address(0)) {
                      revert NotRegistered(registrant);
                  }
                  if (registration != registrant) {
                      revert CannotUpdateWhileSubscribed(registration);
                  }
                  EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrant];
                  uint256 operatorsLength = operators.length;
                  unchecked {
                      if (!filtered) {
                          for (uint256 i = 0; i < operatorsLength; ++i) {
                              address operator = operators[i];
                              bool removed = filteredOperatorsRef.remove(operator);
                              if (!removed) {
                                  revert AddressNotFiltered(operator);
                              }
                          }
                      } else {
                          for (uint256 i = 0; i < operatorsLength; ++i) {
                              address operator = operators[i];
                              bool added = filteredOperatorsRef.add(operator);
                              if (!added) {
                                  revert AddressAlreadyFiltered(operator);
                              }
                          }
                      }
                  }
                  emit OperatorsUpdated(registrant, operators, filtered);
              }
              /**
               * @notice Update multiple codeHashes for a registered address - when filtered is true, the codeHashes will be filtered. Reverts on duplicates.
               */
              function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered)
                  external
                  onlyAddressOrOwner(registrant)
              {
                  address registration = _registrations[registrant];
                  if (registration == address(0)) {
                      revert NotRegistered(registrant);
                  }
                  if (registration != registrant) {
                      revert CannotUpdateWhileSubscribed(registration);
                  }
                  EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrant];
                  uint256 codeHashesLength = codeHashes.length;
                  unchecked {
                      if (!filtered) {
                          for (uint256 i = 0; i < codeHashesLength; ++i) {
                              bytes32 codeHash = codeHashes[i];
                              bool removed = filteredCodeHashesRef.remove(codeHash);
                              if (!removed) {
                                  revert CodeHashNotFiltered(codeHash);
                              }
                          }
                      } else {
                          for (uint256 i = 0; i < codeHashesLength; ++i) {
                              bytes32 codeHash = codeHashes[i];
                              if (codeHash == EOA_CODEHASH) {
                                  revert CannotFilterEOAs();
                              }
                              bool added = filteredCodeHashesRef.add(codeHash);
                              if (!added) {
                                  revert CodeHashAlreadyFiltered(codeHash);
                              }
                          }
                      }
                  }
                  emit CodeHashesUpdated(registrant, codeHashes, filtered);
              }
              /**
               * @notice Subscribe an address to another registrant's filtered operators and codeHashes. Will remove previous
               *         subscription if present.
               *         Note that accounts with subscriptions may go on to subscribe to other accounts - in this case,
               *         subscriptions will not be forwarded. Instead the former subscription's existing entries will still be
               *         used.
               */
              function subscribe(address registrant, address newSubscription) external onlyAddressOrOwner(registrant) {
                  if (registrant == newSubscription) {
                      revert CannotSubscribeToSelf();
                  }
                  if (newSubscription == address(0)) {
                      revert CannotSubscribeToZeroAddress();
                  }
                  address registration = _registrations[registrant];
                  if (registration == address(0)) {
                      revert NotRegistered(registrant);
                  }
                  if (registration == newSubscription) {
                      revert AlreadySubscribed(newSubscription);
                  }
                  address newSubscriptionRegistration = _registrations[newSubscription];
                  if (newSubscriptionRegistration == address(0)) {
                      revert NotRegistered(newSubscription);
                  }
                  if (newSubscriptionRegistration != newSubscription) {
                      revert CannotSubscribeToRegistrantWithSubscription(newSubscription);
                  }
                  if (registration != registrant) {
                      _subscribers[registration].remove(registrant);
                      emit SubscriptionUpdated(registrant, registration, false);
                  }
                  _registrations[registrant] = newSubscription;
                  _subscribers[newSubscription].add(registrant);
                  emit SubscriptionUpdated(registrant, newSubscription, true);
              }
              /**
               * @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
               */
              function unsubscribe(address registrant, bool copyExistingEntries) external onlyAddressOrOwner(registrant) {
                  address registration = _registrations[registrant];
                  if (registration == address(0)) {
                      revert NotRegistered(registrant);
                  }
                  if (registration == registrant) {
                      revert NotSubscribed();
                  }
                  _subscribers[registration].remove(registrant);
                  _registrations[registrant] = registrant;
                  emit SubscriptionUpdated(registrant, registration, false);
                  if (copyExistingEntries) {
                      _copyEntries(registrant, registration);
                  }
              }
              /**
               * @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
               */
              function copyEntriesOf(address registrant, address registrantToCopy) external onlyAddressOrOwner(registrant) {
                  if (registrant == registrantToCopy) {
                      revert CannotCopyFromSelf();
                  }
                  address registration = _registrations[registrant];
                  if (registration == address(0)) {
                      revert NotRegistered(registrant);
                  }
                  if (registration != registrant) {
                      revert CannotUpdateWhileSubscribed(registration);
                  }
                  address registrantRegistration = _registrations[registrantToCopy];
                  if (registrantRegistration == address(0)) {
                      revert NotRegistered(registrantToCopy);
                  }
                  _copyEntries(registrant, registrantToCopy);
              }
              /// @dev helper to copy entries from registrantToCopy to registrant and emit events
              function _copyEntries(address registrant, address registrantToCopy) private {
                  EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrantToCopy];
                  EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrantToCopy];
                  uint256 filteredOperatorsLength = filteredOperatorsRef.length();
                  uint256 filteredCodeHashesLength = filteredCodeHashesRef.length();
                  unchecked {
                      for (uint256 i = 0; i < filteredOperatorsLength; ++i) {
                          address operator = filteredOperatorsRef.at(i);
                          bool added = _filteredOperators[registrant].add(operator);
                          if (added) {
                              emit OperatorUpdated(registrant, operator, true);
                          }
                      }
                      for (uint256 i = 0; i < filteredCodeHashesLength; ++i) {
                          bytes32 codehash = filteredCodeHashesRef.at(i);
                          bool added = _filteredCodeHashes[registrant].add(codehash);
                          if (added) {
                              emit CodeHashUpdated(registrant, codehash, true);
                          }
                      }
                  }
              }
              //////////////////
              // VIEW METHODS //
              //////////////////
              /**
               * @notice Get the subscription address of a given registrant, if any.
               */
              function subscriptionOf(address registrant) external view returns (address subscription) {
                  subscription = _registrations[registrant];
                  if (subscription == address(0)) {
                      revert NotRegistered(registrant);
                  } else if (subscription == registrant) {
                      subscription = address(0);
                  }
              }
              /**
               * @notice Get the set of addresses subscribed to a given registrant.
               *         Note that order is not guaranteed as updates are made.
               */
              function subscribers(address registrant) external view returns (address[] memory) {
                  return _subscribers[registrant].values();
              }
              /**
               * @notice Get the subscriber at a given index in the set of addresses subscribed to a given registrant.
               *         Note that order is not guaranteed as updates are made.
               */
              function subscriberAt(address registrant, uint256 index) external view returns (address) {
                  return _subscribers[registrant].at(index);
              }
              /**
               * @notice Returns true if operator is filtered by a given address or its subscription.
               */
              function isOperatorFiltered(address registrant, address operator) external view returns (bool) {
                  address registration = _registrations[registrant];
                  if (registration != registrant) {
                      return _filteredOperators[registration].contains(operator);
                  }
                  return _filteredOperators[registrant].contains(operator);
              }
              /**
               * @notice Returns true if a codeHash is filtered by a given address or its subscription.
               */
              function isCodeHashFiltered(address registrant, bytes32 codeHash) external view returns (bool) {
                  address registration = _registrations[registrant];
                  if (registration != registrant) {
                      return _filteredCodeHashes[registration].contains(codeHash);
                  }
                  return _filteredCodeHashes[registrant].contains(codeHash);
              }
              /**
               * @notice Returns true if the hash of an address's code is filtered by a given address or its subscription.
               */
              function isCodeHashOfFiltered(address registrant, address operatorWithCode) external view returns (bool) {
                  bytes32 codeHash = operatorWithCode.codehash;
                  address registration = _registrations[registrant];
                  if (registration != registrant) {
                      return _filteredCodeHashes[registration].contains(codeHash);
                  }
                  return _filteredCodeHashes[registrant].contains(codeHash);
              }
              /**
               * @notice Returns true if an address has registered
               */
              function isRegistered(address registrant) external view returns (bool) {
                  return _registrations[registrant] != address(0);
              }
              /**
               * @notice Returns a list of filtered operators for a given address or its subscription.
               */
              function filteredOperators(address registrant) external view returns (address[] memory) {
                  address registration = _registrations[registrant];
                  if (registration != registrant) {
                      return _filteredOperators[registration].values();
                  }
                  return _filteredOperators[registrant].values();
              }
              /**
               * @notice Returns the set of filtered codeHashes for a given address or its subscription.
               *         Note that order is not guaranteed as updates are made.
               */
              function filteredCodeHashes(address registrant) external view returns (bytes32[] memory) {
                  address registration = _registrations[registrant];
                  if (registration != registrant) {
                      return _filteredCodeHashes[registration].values();
                  }
                  return _filteredCodeHashes[registrant].values();
              }
              /**
               * @notice Returns the filtered operator at the given index of the set of filtered operators for a given address or
               *         its subscription.
               *         Note that order is not guaranteed as updates are made.
               */
              function filteredOperatorAt(address registrant, uint256 index) external view returns (address) {
                  address registration = _registrations[registrant];
                  if (registration != registrant) {
                      return _filteredOperators[registration].at(index);
                  }
                  return _filteredOperators[registrant].at(index);
              }
              /**
               * @notice Returns the filtered codeHash at the given index of the list of filtered codeHashes for a given address or
               *         its subscription.
               *         Note that order is not guaranteed as updates are made.
               */
              function filteredCodeHashAt(address registrant, uint256 index) external view returns (bytes32) {
                  address registration = _registrations[registrant];
                  if (registration != registrant) {
                      return _filteredCodeHashes[registration].at(index);
                  }
                  return _filteredCodeHashes[registrant].at(index);
              }
              /// @dev Convenience method to compute the code hash of an arbitrary contract
              function codeHashOf(address a) external view returns (bytes32) {
                  return a.codehash;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          contract OperatorFilterRegistryErrorsAndEvents {
              error CannotFilterEOAs();
              error AddressAlreadyFiltered(address operator);
              error AddressNotFiltered(address operator);
              error CodeHashAlreadyFiltered(bytes32 codeHash);
              error CodeHashNotFiltered(bytes32 codeHash);
              error OnlyAddressOrOwner();
              error NotRegistered(address registrant);
              error AlreadyRegistered();
              error AlreadySubscribed(address subscription);
              error NotSubscribed();
              error CannotUpdateWhileSubscribed(address subscription);
              error CannotSubscribeToSelf();
              error CannotSubscribeToZeroAddress();
              error NotOwnable();
              error AddressFiltered(address filtered);
              error CodeHashFiltered(address account, bytes32 codeHash);
              error CannotSubscribeToRegistrantWithSubscription(address registrant);
              error CannotCopyFromSelf();
              event RegistrationUpdated(address indexed registrant, bool indexed registered);
              event OperatorUpdated(address indexed registrant, address indexed operator, bool indexed filtered);
              event OperatorsUpdated(address indexed registrant, address[] operators, bool indexed filtered);
              event CodeHashUpdated(address indexed registrant, bytes32 indexed codeHash, bool indexed filtered);
              event CodeHashesUpdated(address indexed registrant, bytes32[] codeHashes, bool indexed filtered);
              event SubscriptionUpdated(address indexed registrant, address indexed subscription, bool indexed subscribed);
          }