ETH Price: $2,520.58 (-0.19%)
Gas: 0.47 Gwei

Transaction Decoder

Block:
17116338 at Apr-24-2023 01:06:59 PM +UTC
Transaction Fee:
0.012795013553483475 ETH $32.25
Gas Used:
272,135 Gas / 47.017155285 Gwei

Emitted Events:

102 ERC721SeaDrop.Transfer( from=0x641638a7d826afbc9c4b8d680f7f728b770dbea9, to=[Sender] 0xb1a85b2ac480cd8d2968e32b3fa8484157d49bf1, tokenId=340 )
103 ERC1967Proxy.0x61cbb2a3dee0b6064c2e681aadd61677fb4ef319f0b547508d495626f5a62f64( 0x61cbb2a3dee0b6064c2e681aadd61677fb4ef319f0b547508d495626f5a62f64, 0x000000000000000000000000641638a7d826afbc9c4b8d680f7f728b770dbea9, 0x000000000000000000000000b1a85b2ac480cd8d2968e32b3fa8484157d49bf1, 0000000000000000000000000000000000000000000000000000000000000080, b0532749aa5d2a5b394959b309b87adf56f06dbafca1cb6f1f0831d87c16da56, 0000000000000000000000000000000000000000000000000000000000000280, c21d2a2a00062e052587b40fe3935ec60ceb546d5010defe1968c1526242fd80, 000000000000000000000000641638a7d826afbc9c4b8d680f7f728b770dbea9, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000dab4a563819e8fd93dba3b25bc3495, 000000000000000000000000c326f583655e2bb0a2b1e3e246df81d6875a06e5, 0000000000000000000000000000000000000000000000000000000000000154, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000e8aaceda572000, 0000000000000000000000000000000000000000000000000000000064467e07, 00000000000000000000000000000000000000000000000000000000644a7287, 00000000000000000000000000000000000000000000000000000000000001a0, 00000000000000000000000000000000ab40d40d737ce0659ea709f8105e9fc1, 00000000000000000000000000000000000000000000000000000000000001c0, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000001, 0100000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000b1a85b2ac480cd8d2968e32b3fa8484157d49bf1, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000dab4a563819e8fd93dba3b25bc3495, 000000000000000000000000c326f583655e2bb0a2b1e3e246df81d6875a06e5, 0000000000000000000000000000000000000000000000000000000000000154, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000e8aaceda572000, 0000000000000000000000000000000000000000000000000000000064467e08, 0000000000000000000000000000000000000000000000000000000064468cf8, 00000000000000000000000000000000000000000000000000000000000001a0, 000000000000000000000000000000004f8a46dde7a24500807a59ec1b8a61eb, 00000000000000000000000000000000000000000000000000000000000001c0, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000001, 0100000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...830B95127
(Blur.io: Marketplace)
0x641638A7...b770DbEA9 0.429978050527536888 Eth0.495468050527536888 Eth0.06549
0xB1a85b2a...157D49BF1
0.173512610871677 Eth
Nonce: 8
0.095227597318193525 Eth
Nonce: 9
0.078285013553483475
0xc326F583...6875A06e5
(Fee Recipient: 0xe68...127)
10,570.3023645315307328 Eth10,570.3027727340307328 Eth0.0004082025

Execution Trace

ETH 0.06549 ERC1967Proxy.9a1fc3a7( )
  • ETH 0.06549 BlurExchange.execute( sell=[{name:order, type:tuple, order:1, indexed:false, value:[{name:trader, type:address, order:1, indexed:false, value:0x641638A7d826AfBc9C4B8d680F7F728b770DbEA9, valueString:0x641638A7d826AfBc9C4B8d680F7F728b770DbEA9}, {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:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, valueString:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5}, {name:tokenId, type:uint256, order:5, indexed:false, value:340, valueString:340}, {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:65490000000000000, valueString:65490000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1682341383, valueString:1682341383}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1682600583, valueString:1682600583}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:227634595214710672690075552912693436353, valueString:227634595214710672690075552912693436353}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}], valueString:[{name:trader, type:address, order:1, indexed:false, value:0x641638A7d826AfBc9C4B8d680F7F728b770DbEA9, valueString:0x641638A7d826AfBc9C4B8d680F7F728b770DbEA9}, {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:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, valueString:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5}, {name:tokenId, type:uint256, order:5, indexed:false, value:340, valueString:340}, {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:65490000000000000, valueString:65490000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1682341383, valueString:1682341383}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1682600583, valueString:1682600583}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:227634595214710672690075552912693436353, valueString:227634595214710672690075552912693436353}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}]}, {name:v, type:uint8, order:2, indexed:false, value:28, valueString:28}, {name:r, type:bytes32, order:3, indexed:false, value:B15031A320AC4DDDD85809D7DAC75C3F64A023F5CB05408496DA78CD2B84B506, valueString:B15031A320AC4DDDD85809D7DAC75C3F64A023F5CB05408496DA78CD2B84B506}, {name:s, type:bytes32, order:4, indexed:false, value:0B10CE2521EAF35FDECB32728973C4C5A281A437D7C71B29F13BD1E423FFF74C, valueString:0B10CE2521EAF35FDECB32728973C4C5A281A437D7C71B29F13BD1E423FFF74C}, {name:extraSignature, type:bytes, order:5, indexed:false, value:0x000000000000000000000000000000000000000000000000000000000000001C0002E6AA99FFE356EEA07CFB988F94333A9E17007A0FFFA063BE9EC79BCB72B75788A1BD819B41E999EDDF07268DEA9494BA2806C56BFC958F22E02D5668BC72, valueString:0x000000000000000000000000000000000000000000000000000000000000001C0002E6AA99FFE356EEA07CFB988F94333A9E17007A0FFFA063BE9EC79BCB72B75788A1BD819B41E999EDDF07268DEA9494BA2806C56BFC958F22E02D5668BC72}, {name:signatureVersion, type:uint8, order:6, indexed:false, value:0, valueString:0}, {name:blockNumber, type:uint256, order:7, indexed:false, value:17116336, valueString:17116336}], buy=[{name:order, type:tuple, order:1, indexed:false, value:[{name:trader, type:address, order:1, indexed:false, value:0xB1a85b2ac480CD8D2968E32B3Fa8484157D49BF1, valueString:0xB1a85b2ac480CD8D2968E32B3Fa8484157D49BF1}, {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:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, valueString:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5}, {name:tokenId, type:uint256, order:5, indexed:false, value:340, valueString:340}, {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:65490000000000000, valueString:65490000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1682341384, valueString:1682341384}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1682345208, valueString:1682345208}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:105726985983269540545539660398251893227, valueString:105726985983269540545539660398251893227}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}], valueString:[{name:trader, type:address, order:1, indexed:false, value:0xB1a85b2ac480CD8D2968E32B3Fa8484157D49BF1, valueString:0xB1a85b2ac480CD8D2968E32B3Fa8484157D49BF1}, {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:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, valueString:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5}, {name:tokenId, type:uint256, order:5, indexed:false, value:340, valueString:340}, {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:65490000000000000, valueString:65490000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1682341384, valueString:1682341384}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1682345208, valueString:1682345208}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:105726985983269540545539660398251893227, valueString:105726985983269540545539660398251893227}, {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:0x000000000000000000000000000000000000000000000000000000000000001BFEE68818A6ED3F9426849C29B23EEE1DF55E94DA11E14E793D57DE20587589385C135F73EA0FAF1C7559A16F1EE81F036C3941CCF7CBA43D94DE43F4BD21FE9C, valueString:0x000000000000000000000000000000000000000000000000000000000000001BFEE68818A6ED3F9426849C29B23EEE1DF55E94DA11E14E793D57DE20587589385C135F73EA0FAF1C7559A16F1EE81F036C3941CCF7CBA43D94DE43F4BD21FE9C}, {name:signatureVersion, type:uint8, order:6, indexed:false, value:0, valueString:0}, {name:blockNumber, type:uint256, order:7, indexed:false, value:17116336, valueString:17116336}] )
    • Null: 0x000...001.b8aa487d( )
    • Null: 0x000...001.1ce19a16( )
    • Null: 0x000...001.0f3a930b( )
    • PolicyManager.isPolicyWhitelisted( policy=0x0000000000daB4A563819e8fd93dbA3b25BC3495 ) => ( True )
    • StandardPolicyERC721.canMatchMakerAsk( makerAsk=[{name:trader, type:address, order:1, indexed:false, value:0x641638A7d826AfBc9C4B8d680F7F728b770DbEA9, valueString:0x641638A7d826AfBc9C4B8d680F7F728b770DbEA9}, {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:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, valueString:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5}, {name:tokenId, type:uint256, order:5, indexed:false, value:340, valueString:340}, {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:65490000000000000, valueString:65490000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1682341383, valueString:1682341383}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1682600583, valueString:1682600583}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:227634595214710672690075552912693436353, valueString:227634595214710672690075552912693436353}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}], takerBid=[{name:trader, type:address, order:1, indexed:false, value:0xB1a85b2ac480CD8D2968E32B3Fa8484157D49BF1, valueString:0xB1a85b2ac480CD8D2968E32B3Fa8484157D49BF1}, {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:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, valueString:0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5}, {name:tokenId, type:uint256, order:5, indexed:false, value:340, valueString:340}, {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:65490000000000000, valueString:65490000000000000}, {name:listingTime, type:uint256, order:9, indexed:false, value:1682341384, valueString:1682341384}, {name:expirationTime, type:uint256, order:10, indexed:false, value:1682345208, valueString:1682345208}, {name:fees, type:tuple[], order:11, indexed:false}, {name:salt, type:uint256, order:12, indexed:false, value:105726985983269540545539660398251893227, valueString:105726985983269540545539660398251893227}, {name:extraParams, type:bytes, order:13, indexed:false, value:0x01, valueString:0x01}] ) => ( True, 65490000000000000, 340, 1, 0 )
    • ETH 0.06549 0x641638a7d826afbc9c4b8d680f7f728b770dbea9.CALL( )
    • ExecutionDelegate.transferERC721( collection=0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, from=0x641638A7d826AfBc9C4B8d680F7F728b770DbEA9, to=0xB1a85b2ac480CD8D2968E32B3Fa8484157D49BF1, tokenId=340 )
      • ERC721SeaDrop.safeTransferFrom( from=0x641638A7d826AfBc9C4B8d680F7F728b770DbEA9, to=0xB1a85b2ac480CD8D2968E32B3Fa8484157D49BF1, tokenId=340 )
        • OperatorFilterRegistry.isOperatorAllowed( registrant=0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, operator=0x00000000000111AbE46ff893f3B2fdF1F759a8A8 ) => ( True )
        • OperatorFilterRegistry.isOperatorAllowed( registrant=0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, operator=0x00000000000111AbE46ff893f3B2fdF1F759a8A8 ) => ( True )
        • OperatorFilterRegistry.isOperatorAllowed( registrant=0xc326F583655E2bB0a2b1E3E246Df81D6875A06e5, operator=0x00000000000111AbE46ff893f3B2fdF1F759a8A8 ) => ( True )
          execute[BlurExchange (ln:126)]
          File 1 of 7: ERC1967Proxy
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)
          pragma solidity 0.8.17;
          // OpenZeppelin Contracts v4.4.1 (proxy/Proxy.sol)
          /**
           * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
           * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
           * be specified by overriding the virtual {_implementation} function.
           *
           * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
           * different contract through the {_delegate} function.
           *
           * The success and return data of the delegated call will be returned back to the caller of the proxy.
           */
          abstract contract Proxy {
              /**
               * @dev Delegates the current call to `implementation`.
               *
               * This function does not return to its internall call site, it will return directly to the external caller.
               */
              function _delegate(address implementation) internal virtual {
                  assembly {
                      // Copy msg.data. We take full control of memory in this inline assembly
                      // block because it will not return to Solidity code. We overwrite the
                      // Solidity scratch pad at memory position 0.
                      calldatacopy(0, 0, calldatasize())
                      // Call the implementation.
                      // out and outsize are 0 because we don't know the size yet.
                      let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                      // Copy the returned data.
                      returndatacopy(0, 0, returndatasize())
                      switch result
                      // delegatecall returns 0 on error.
                      case 0 {
                          revert(0, returndatasize())
                      }
                      default {
                          return(0, returndatasize())
                      }
                  }
              }
              /**
               * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
               * and {_fallback} should delegate.
               */
              function _implementation() internal view virtual returns (address);
              /**
               * @dev Delegates the current call to the address returned by `_implementation()`.
               *
               * This function does not return to its internall call site, it will return directly to the external caller.
               */
              function _fallback() internal virtual {
                  _beforeFallback();
                  _delegate(_implementation());
              }
              /**
               * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
               * function in the contract matches the call data.
               */
              fallback() external payable virtual {
                  _fallback();
              }
              /**
               * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
               * is empty.
               */
              receive() external payable virtual {
                  _fallback();
              }
              /**
               * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
               * call, or as part of the Solidity `fallback` or `receive` functions.
               *
               * If overriden should call `super._beforeFallback()`.
               */
              function _beforeFallback() internal virtual {}
          }
          // OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Upgrade.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 ERC1967Upgrade {
              // 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 StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
              }
              /**
               * @dev Stores a new address in the EIP1967 implementation slot.
               */
              function _setImplementation(address newImplementation) private {
                  require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                  StorageSlot.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) {
                      Address.functionDelegateCall(newImplementation, data);
                  }
              }
              /**
               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCallSecure(
                  address newImplementation,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  address oldImplementation = _getImplementation();
                  // Initial upgrade and setup call
                  _setImplementation(newImplementation);
                  if (data.length > 0 || forceCall) {
                      Address.functionDelegateCall(newImplementation, data);
                  }
                  // Perform rollback test if not already in progress
                  StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                  if (!rollbackTesting.value) {
                      // Trigger rollback using upgradeTo from the new implementation
                      rollbackTesting.value = true;
                      Address.functionDelegateCall(
                          newImplementation,
                          abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
                      );
                      rollbackTesting.value = false;
                      // Check rollback was effective
                      require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                      // Finally reset to the new implementation and log the upgrade
                      _upgradeTo(newImplementation);
                  }
              }
              /**
               * @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 StorageSlot.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");
                  StorageSlot.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 StorageSlot.getAddressSlot(_BEACON_SLOT).value;
              }
              /**
               * @dev Stores a new beacon in the EIP1967 beacon slot.
               */
              function _setBeacon(address newBeacon) private {
                  require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                  require(
                      Address.isContract(IBeacon(newBeacon).implementation()),
                      "ERC1967: beacon implementation is not a contract"
                  );
                  StorageSlot.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) {
                      Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                  }
              }
          }
          /**
           * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
           * implementation address that can be changed. This address is stored in storage in the location specified by
           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
           * implementation behind the proxy.
           */
          contract ERC1967Proxy is Proxy, ERC1967Upgrade {
              /**
               * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
               *
               * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
               * function call, and allows initializating the storage of the proxy like a Solidity constructor.
               */
              constructor(address _logic, bytes memory _data) payable {
                  assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                  _upgradeToAndCall(_logic, _data, false);
              }
              /**
               * @dev Returns the current implementation address.
               */
              function _implementation() internal view virtual override returns (address impl) {
                  return ERC1967Upgrade._getImplementation();
              }
          }
          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
          /**
           * @dev This is the interface that {BeaconProxy} expects of its beacon.
           */
          interface IBeacon {
              /**
               * @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);
          }
          // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
          pragma solidity ^0.8.0;
          /**
           * @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
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize, which returns 0 for contracts in
                  // construction, since the code is only stored at the end of the
                  // constructor execution.
                  uint256 size;
                  assembly {
                      size := extcodesize(account)
                  }
                  return size > 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);
                      }
                  }
              }
          }
          // OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)
          /**
           * @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 StorageSlot {
              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) {
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
               */
              function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
               */
              function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
               */
              function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                  assembly {
                      r.slot := slot
                  }
              }
          }

          File 2 of 7: ERC721SeaDrop
          // SPDX-License-Identifier: MIT
          // ERC721A Contracts v4.2.2
          // Creator: Chiru Labs
          pragma solidity ^0.8.4;
          import './IERC721A.sol';
          /**
           * @dev Interface of ERC721 token receiver.
           */
          interface ERC721A__IERC721Receiver {
              function onERC721Received(
                  address operator,
                  address from,
                  uint256 tokenId,
                  bytes calldata data
              ) external returns (bytes4);
          }
          /**
           * @title ERC721A
           *
           * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
           * Non-Fungible Token Standard, including the Metadata extension.
           * Optimized for lower gas during batch mints.
           *
           * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
           * starting from `_startTokenId()`.
           *
           * Assumptions:
           *
           * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
           * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
           */
          contract ERC721A is IERC721A {
              // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
              struct TokenApprovalRef {
                  address value;
              }
              // =============================================================
              //                           CONSTANTS
              // =============================================================
              // Mask of an entry in packed address data.
              uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
              // The bit position of `numberMinted` in packed address data.
              uint256 private constant _BITPOS_NUMBER_MINTED = 64;
              // The bit position of `numberBurned` in packed address data.
              uint256 private constant _BITPOS_NUMBER_BURNED = 128;
              // The bit position of `aux` in packed address data.
              uint256 private constant _BITPOS_AUX = 192;
              // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
              uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
              // The bit position of `startTimestamp` in packed ownership.
              uint256 private constant _BITPOS_START_TIMESTAMP = 160;
              // The bit mask of the `burned` bit in packed ownership.
              uint256 private constant _BITMASK_BURNED = 1 << 224;
              // The bit position of the `nextInitialized` bit in packed ownership.
              uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
              // The bit mask of the `nextInitialized` bit in packed ownership.
              uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
              // The bit position of `extraData` in packed ownership.
              uint256 private constant _BITPOS_EXTRA_DATA = 232;
              // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
              uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
              // The mask of the lower 160 bits for addresses.
              uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
              // The maximum `quantity` that can be minted with {_mintERC2309}.
              // This limit is to prevent overflows on the address data entries.
              // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
              // is required to cause an overflow, which is unrealistic.
              uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
              // The `Transfer` event signature is given by:
              // `keccak256(bytes("Transfer(address,address,uint256)"))`.
              bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
                  0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
              // =============================================================
              //                            STORAGE
              // =============================================================
              // The next token ID to be minted.
              uint256 private _currentIndex;
              // The number of tokens burned.
              uint256 private _burnCounter;
              // 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 {_packedOwnershipOf} implementation for details.
              //
              // Bits Layout:
              // - [0..159]   `addr`
              // - [160..223] `startTimestamp`
              // - [224]      `burned`
              // - [225]      `nextInitialized`
              // - [232..255] `extraData`
              mapping(uint256 => uint256) private _packedOwnerships;
              // Mapping owner address to address data.
              //
              // Bits Layout:
              // - [0..63]    `balance`
              // - [64..127]  `numberMinted`
              // - [128..191] `numberBurned`
              // - [192..255] `aux`
              mapping(address => uint256) private _packedAddressData;
              // Mapping from token ID to approved address.
              mapping(uint256 => TokenApprovalRef) private _tokenApprovals;
              // Mapping from owner to operator approvals
              mapping(address => mapping(address => bool)) private _operatorApprovals;
              // =============================================================
              //                          CONSTRUCTOR
              // =============================================================
              constructor(string memory name_, string memory symbol_) {
                  _name = name_;
                  _symbol = symbol_;
                  _currentIndex = _startTokenId();
              }
              // =============================================================
              //                   TOKEN COUNTING OPERATIONS
              // =============================================================
              /**
               * @dev Returns the starting token ID.
               * To change the starting token ID, please override this function.
               */
              function _startTokenId() internal view virtual returns (uint256) {
                  return 0;
              }
              /**
               * @dev Returns the next token ID to be minted.
               */
              function _nextTokenId() internal view virtual returns (uint256) {
                  return _currentIndex;
              }
              /**
               * @dev Returns the total number of tokens in existence.
               * Burned tokens will reduce the count.
               * To get the total number of tokens minted, please see {_totalMinted}.
               */
              function totalSupply() public view virtual override returns (uint256) {
                  // Counter underflow is impossible as _burnCounter cannot be incremented
                  // more than `_currentIndex - _startTokenId()` times.
                  unchecked {
                      return _currentIndex - _burnCounter - _startTokenId();
                  }
              }
              /**
               * @dev Returns the total amount of tokens minted in the contract.
               */
              function _totalMinted() internal view virtual returns (uint256) {
                  // Counter underflow is impossible as `_currentIndex` does not decrement,
                  // and it is initialized to `_startTokenId()`.
                  unchecked {
                      return _currentIndex - _startTokenId();
                  }
              }
              /**
               * @dev Returns the total number of tokens burned.
               */
              function _totalBurned() internal view virtual returns (uint256) {
                  return _burnCounter;
              }
              // =============================================================
              //                    ADDRESS DATA OPERATIONS
              // =============================================================
              /**
               * @dev Returns the number of tokens in `owner`'s account.
               */
              function balanceOf(address owner) public view virtual override returns (uint256) {
                  if (owner == address(0)) revert BalanceQueryForZeroAddress();
                  return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
              }
              /**
               * Returns the number of tokens minted by `owner`.
               */
              function _numberMinted(address owner) internal view returns (uint256) {
                  return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
              }
              /**
               * Returns the number of tokens burned by or on behalf of `owner`.
               */
              function _numberBurned(address owner) internal view returns (uint256) {
                  return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
              }
              /**
               * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
               */
              function _getAux(address owner) internal view returns (uint64) {
                  return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
              }
              /**
               * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
               * If there are multiple variables, please pack them into a uint64.
               */
              function _setAux(address owner, uint64 aux) internal virtual {
                  uint256 packed = _packedAddressData[owner];
                  uint256 auxCasted;
                  // Cast `aux` with assembly to avoid redundant masking.
                  assembly {
                      auxCasted := aux
                  }
                  packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
                  _packedAddressData[owner] = packed;
              }
              // =============================================================
              //                            IERC165
              // =============================================================
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30000 gas.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  // The interface IDs are constants representing the first 4 bytes
                  // of the XOR of all function selectors in the interface.
                  // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
                  // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
                  return
                      interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
                      interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
                      interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
              }
              // =============================================================
              //                        IERC721Metadata
              // =============================================================
              /**
               * @dev Returns the token collection name.
               */
              function name() public view virtual override returns (string memory) {
                  return _name;
              }
              /**
               * @dev Returns the token collection symbol.
               */
              function symbol() public view virtual override returns (string memory) {
                  return _symbol;
              }
              /**
               * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
               */
              function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                  if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
                  string memory baseURI = _baseURI();
                  return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
              }
              /**
               * @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, it can be overridden in child contracts.
               */
              function _baseURI() internal view virtual returns (string memory) {
                  return '';
              }
              // =============================================================
              //                     OWNERSHIPS OPERATIONS
              // =============================================================
              /**
               * @dev Returns the owner of the `tokenId` token.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               */
              function ownerOf(uint256 tokenId) public view virtual override returns (address) {
                  return address(uint160(_packedOwnershipOf(tokenId)));
              }
              /**
               * @dev Gas spent here starts off proportional to the maximum mint batch size.
               * It gradually moves to O(1) as tokens get transferred around over time.
               */
              function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
                  return _unpackedOwnership(_packedOwnershipOf(tokenId));
              }
              /**
               * @dev Returns the unpacked `TokenOwnership` struct at `index`.
               */
              function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
                  return _unpackedOwnership(_packedOwnerships[index]);
              }
              /**
               * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
               */
              function _initializeOwnershipAt(uint256 index) internal virtual {
                  if (_packedOwnerships[index] == 0) {
                      _packedOwnerships[index] = _packedOwnershipOf(index);
                  }
              }
              /**
               * Returns the packed ownership data of `tokenId`.
               */
              function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
                  uint256 curr = tokenId;
                  unchecked {
                      if (_startTokenId() <= curr)
                          if (curr < _currentIndex) {
                              uint256 packed = _packedOwnerships[curr];
                              // If not burned.
                              if (packed & _BITMASK_BURNED == 0) {
                                  // Invariant:
                                  // There will always be an initialized ownership slot
                                  // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                                  // before an unintialized ownership slot
                                  // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                                  // Hence, `curr` will not underflow.
                                  //
                                  // We can directly compare the packed value.
                                  // If the address is zero, packed will be zero.
                                  while (packed == 0) {
                                      packed = _packedOwnerships[--curr];
                                  }
                                  return packed;
                              }
                          }
                  }
                  revert OwnerQueryForNonexistentToken();
              }
              /**
               * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
               */
              function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
                  ownership.addr = address(uint160(packed));
                  ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
                  ownership.burned = packed & _BITMASK_BURNED != 0;
                  ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
              }
              /**
               * @dev Packs ownership data into a single uint256.
               */
              function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
                  assembly {
                      // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      owner := and(owner, _BITMASK_ADDRESS)
                      // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                      result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
                  }
              }
              /**
               * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
               */
              function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
                  // For branchless setting of the `nextInitialized` flag.
                  assembly {
                      // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                      result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
                  }
              }
              // =============================================================
              //                      APPROVAL OPERATIONS
              // =============================================================
              /**
               * @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) public virtual override {
                  address owner = ownerOf(tokenId);
                  if (_msgSenderERC721A() != owner)
                      if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                          revert ApprovalCallerNotOwnerNorApproved();
                      }
                  _tokenApprovals[tokenId].value = to;
                  emit Approval(owner, to, tokenId);
              }
              /**
               * @dev Returns the account approved for `tokenId` token.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               */
              function getApproved(uint256 tokenId) public view virtual override returns (address) {
                  if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                  return _tokenApprovals[tokenId].value;
              }
              /**
               * @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) public virtual override {
                  _operatorApprovals[_msgSenderERC721A()][operator] = approved;
                  emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
              }
              /**
               * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
               *
               * See {setApprovalForAll}.
               */
              function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                  return _operatorApprovals[owner][operator];
              }
              /**
               * @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. See {_mint}.
               */
              function _exists(uint256 tokenId) internal view virtual returns (bool) {
                  return
                      _startTokenId() <= tokenId &&
                      tokenId < _currentIndex && // If within bounds,
                      _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
              }
              /**
               * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
               */
              function _isSenderApprovedOrOwner(
                  address approvedAddress,
                  address owner,
                  address msgSender
              ) private pure returns (bool result) {
                  assembly {
                      // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      owner := and(owner, _BITMASK_ADDRESS)
                      // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      msgSender := and(msgSender, _BITMASK_ADDRESS)
                      // `msgSender == owner || msgSender == approvedAddress`.
                      result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
                  }
              }
              /**
               * @dev Returns the storage slot and value for the approved address of `tokenId`.
               */
              function _getApprovedSlotAndAddress(uint256 tokenId)
                  private
                  view
                  returns (uint256 approvedAddressSlot, address approvedAddress)
              {
                  TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
                  // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
                  assembly {
                      approvedAddressSlot := tokenApproval.slot
                      approvedAddress := sload(approvedAddressSlot)
                  }
              }
              // =============================================================
              //                      TRANSFER OPERATIONS
              // =============================================================
              /**
               * @dev Transfers `tokenId` from `from` to `to`.
               *
               * 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
              ) public virtual override {
                  uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
                  if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
                  (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
                  // The nested ifs save around 20+ gas over a compound boolean condition.
                  if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                      if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
                  if (to == address(0)) revert TransferToZeroAddress();
                  _beforeTokenTransfers(from, to, tokenId, 1);
                  // Clear approvals from the previous owner.
                  assembly {
                      if approvedAddress {
                          // This is equivalent to `delete _tokenApprovals[tokenId]`.
                          sstore(approvedAddressSlot, 0)
                      }
                  }
                  // Underflow of the sender's balance is impossible because we check for
                  // ownership above and the recipient's balance can't realistically overflow.
                  // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
                  unchecked {
                      // We can directly increment and decrement the balances.
                      --_packedAddressData[from]; // Updates: `balance -= 1`.
                      ++_packedAddressData[to]; // Updates: `balance += 1`.
                      // Updates:
                      // - `address` to the next owner.
                      // - `startTimestamp` to the timestamp of transfering.
                      // - `burned` to `false`.
                      // - `nextInitialized` to `true`.
                      _packedOwnerships[tokenId] = _packOwnershipData(
                          to,
                          _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
                      );
                      // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                      if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                          uint256 nextTokenId = tokenId + 1;
                          // If the next slot's address is zero and not burned (i.e. packed value is zero).
                          if (_packedOwnerships[nextTokenId] == 0) {
                              // If the next slot is within bounds.
                              if (nextTokenId != _currentIndex) {
                                  // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                                  _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                              }
                          }
                      }
                  }
                  emit Transfer(from, to, tokenId);
                  _afterTokenTransfers(from, to, tokenId, 1);
              }
              /**
               * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) public virtual override {
                  safeTransferFrom(from, to, tokenId, '');
              }
              /**
               * @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 memory _data
              ) public virtual override {
                  transferFrom(from, to, tokenId);
                  if (to.code.length != 0)
                      if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                          revert TransferToNonERC721ReceiverImplementer();
                      }
              }
              /**
               * @dev Hook that is called before a set of serially-ordered token IDs
               * are about to be transferred. This includes minting.
               * And also called before burning one token.
               *
               * `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`.
               * - When `to` is zero, `tokenId` will be burned by `from`.
               * - `from` and `to` are never both zero.
               */
              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.
               * And also called after one token has been burned.
               *
               * `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` has been
               * transferred to `to`.
               * - When `from` is zero, `tokenId` has been minted for `to`.
               * - When `to` is zero, `tokenId` has been burned by `from`.
               * - `from` and `to` are never both zero.
               */
              function _afterTokenTransfers(
                  address from,
                  address to,
                  uint256 startTokenId,
                  uint256 quantity
              ) internal virtual {}
              /**
               * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
               *
               * `from` - Previous owner of the given token ID.
               * `to` - Target address that will receive the token.
               * `tokenId` - Token ID to be transferred.
               * `_data` - Optional data to send along with the call.
               *
               * Returns whether the call correctly returned the expected magic value.
               */
              function _checkContractOnERC721Received(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) private returns (bool) {
                  try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
                      bytes4 retval
                  ) {
                      return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
                  } catch (bytes memory reason) {
                      if (reason.length == 0) {
                          revert TransferToNonERC721ReceiverImplementer();
                      } else {
                          assembly {
                              revert(add(32, reason), mload(reason))
                          }
                      }
                  }
              }
              // =============================================================
              //                        MINT OPERATIONS
              // =============================================================
              /**
               * @dev Mints `quantity` tokens and transfers them to `to`.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - `quantity` must be greater than 0.
               *
               * Emits a {Transfer} event for each mint.
               */
              function _mint(address to, uint256 quantity) internal virtual {
                  uint256 startTokenId = _currentIndex;
                  if (quantity == 0) revert MintZeroQuantity();
                  _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                  // Overflows are incredibly unrealistic.
                  // `balance` and `numberMinted` have a maximum limit of 2**64.
                  // `tokenId` has a maximum limit of 2**256.
                  unchecked {
                      // Updates:
                      // - `balance += quantity`.
                      // - `numberMinted += quantity`.
                      //
                      // We can directly add to the `balance` and `numberMinted`.
                      _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
                      // Updates:
                      // - `address` to the owner.
                      // - `startTimestamp` to the timestamp of minting.
                      // - `burned` to `false`.
                      // - `nextInitialized` to `quantity == 1`.
                      _packedOwnerships[startTokenId] = _packOwnershipData(
                          to,
                          _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                      );
                      uint256 toMasked;
                      uint256 end = startTokenId + quantity;
                      // Use assembly to loop and emit the `Transfer` event for gas savings.
                      // The duplicated `log4` removes an extra check and reduces stack juggling.
                      // The assembly, together with the surrounding Solidity code, have been
                      // delicately arranged to nudge the compiler into producing optimized opcodes.
                      assembly {
                          // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                          toMasked := and(to, _BITMASK_ADDRESS)
                          // Emit the `Transfer` event.
                          log4(
                              0, // Start of data (0, since no data).
                              0, // End of data (0, since no data).
                              _TRANSFER_EVENT_SIGNATURE, // Signature.
                              0, // `address(0)`.
                              toMasked, // `to`.
                              startTokenId // `tokenId`.
                          )
                          // The `iszero(eq(,))` check ensures that large values of `quantity`
                          // that overflows uint256 will make the loop run out of gas.
                          // The compiler will optimize the `iszero` away for performance.
                          for {
                              let tokenId := add(startTokenId, 1)
                          } iszero(eq(tokenId, end)) {
                              tokenId := add(tokenId, 1)
                          } {
                              // Emit the `Transfer` event. Similar to above.
                              log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                          }
                      }
                      if (toMasked == 0) revert MintToZeroAddress();
                      _currentIndex = end;
                  }
                  _afterTokenTransfers(address(0), to, startTokenId, quantity);
              }
              /**
               * @dev Mints `quantity` tokens and transfers them to `to`.
               *
               * This function is intended for efficient minting only during contract creation.
               *
               * It emits only one {ConsecutiveTransfer} as defined in
               * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
               * instead of a sequence of {Transfer} event(s).
               *
               * Calling this function outside of contract creation WILL make your contract
               * non-compliant with the ERC721 standard.
               * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
               * {ConsecutiveTransfer} event is only permissible during contract creation.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - `quantity` must be greater than 0.
               *
               * Emits a {ConsecutiveTransfer} event.
               */
              function _mintERC2309(address to, uint256 quantity) internal virtual {
                  uint256 startTokenId = _currentIndex;
                  if (to == address(0)) revert MintToZeroAddress();
                  if (quantity == 0) revert MintZeroQuantity();
                  if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
                  _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                  // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
                  unchecked {
                      // Updates:
                      // - `balance += quantity`.
                      // - `numberMinted += quantity`.
                      //
                      // We can directly add to the `balance` and `numberMinted`.
                      _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
                      // Updates:
                      // - `address` to the owner.
                      // - `startTimestamp` to the timestamp of minting.
                      // - `burned` to `false`.
                      // - `nextInitialized` to `quantity == 1`.
                      _packedOwnerships[startTokenId] = _packOwnershipData(
                          to,
                          _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                      );
                      emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
                      _currentIndex = startTokenId + quantity;
                  }
                  _afterTokenTransfers(address(0), to, startTokenId, quantity);
              }
              /**
               * @dev Safely mints `quantity` tokens and transfers them to `to`.
               *
               * Requirements:
               *
               * - If `to` refers to a smart contract, it must implement
               * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
               * - `quantity` must be greater than 0.
               *
               * See {_mint}.
               *
               * Emits a {Transfer} event for each mint.
               */
              function _safeMint(
                  address to,
                  uint256 quantity,
                  bytes memory _data
              ) internal virtual {
                  _mint(to, quantity);
                  unchecked {
                      if (to.code.length != 0) {
                          uint256 end = _currentIndex;
                          uint256 index = end - quantity;
                          do {
                              if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                                  revert TransferToNonERC721ReceiverImplementer();
                              }
                          } while (index < end);
                          // Reentrancy protection.
                          if (_currentIndex != end) revert();
                      }
                  }
              }
              /**
               * @dev Equivalent to `_safeMint(to, quantity, '')`.
               */
              function _safeMint(address to, uint256 quantity) internal virtual {
                  _safeMint(to, quantity, '');
              }
              // =============================================================
              //                        BURN OPERATIONS
              // =============================================================
              /**
               * @dev Equivalent to `_burn(tokenId, false)`.
               */
              function _burn(uint256 tokenId) internal virtual {
                  _burn(tokenId, false);
              }
              /**
               * @dev Destroys `tokenId`.
               * The approval is cleared when the token is burned.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               *
               * Emits a {Transfer} event.
               */
              function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
                  uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
                  address from = address(uint160(prevOwnershipPacked));
                  (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
                  if (approvalCheck) {
                      // The nested ifs save around 20+ gas over a compound boolean condition.
                      if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                          if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
                  }
                  _beforeTokenTransfers(from, address(0), tokenId, 1);
                  // Clear approvals from the previous owner.
                  assembly {
                      if approvedAddress {
                          // This is equivalent to `delete _tokenApprovals[tokenId]`.
                          sstore(approvedAddressSlot, 0)
                      }
                  }
                  // Underflow of the sender's balance is impossible because we check for
                  // ownership above and the recipient's balance can't realistically overflow.
                  // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
                  unchecked {
                      // Updates:
                      // - `balance -= 1`.
                      // - `numberBurned += 1`.
                      //
                      // We can directly decrement the balance, and increment the number burned.
                      // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
                      _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
                      // Updates:
                      // - `address` to the last owner.
                      // - `startTimestamp` to the timestamp of burning.
                      // - `burned` to `true`.
                      // - `nextInitialized` to `true`.
                      _packedOwnerships[tokenId] = _packOwnershipData(
                          from,
                          (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
                      );
                      // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                      if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                          uint256 nextTokenId = tokenId + 1;
                          // If the next slot's address is zero and not burned (i.e. packed value is zero).
                          if (_packedOwnerships[nextTokenId] == 0) {
                              // If the next slot is within bounds.
                              if (nextTokenId != _currentIndex) {
                                  // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                                  _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                              }
                          }
                      }
                  }
                  emit Transfer(from, address(0), tokenId);
                  _afterTokenTransfers(from, address(0), tokenId, 1);
                  // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
                  unchecked {
                      _burnCounter++;
                  }
              }
              // =============================================================
              //                     EXTRA DATA OPERATIONS
              // =============================================================
              /**
               * @dev Directly sets the extra data for the ownership data `index`.
               */
              function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
                  uint256 packed = _packedOwnerships[index];
                  if (packed == 0) revert OwnershipNotInitializedForExtraData();
                  uint256 extraDataCasted;
                  // Cast `extraData` with assembly to avoid redundant masking.
                  assembly {
                      extraDataCasted := extraData
                  }
                  packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
                  _packedOwnerships[index] = packed;
              }
              /**
               * @dev Called during each token transfer to set the 24bit `extraData` field.
               * Intended to be overridden by the cosumer contract.
               *
               * `previousExtraData` - the value of `extraData` before transfer.
               *
               * 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`.
               * - When `to` is zero, `tokenId` will be burned by `from`.
               * - `from` and `to` are never both zero.
               */
              function _extraData(
                  address from,
                  address to,
                  uint24 previousExtraData
              ) internal view virtual returns (uint24) {}
              /**
               * @dev Returns the next extra data for the packed ownership data.
               * The returned result is shifted into position.
               */
              function _nextExtraData(
                  address from,
                  address to,
                  uint256 prevOwnershipPacked
              ) private view returns (uint256) {
                  uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
                  return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
              }
              // =============================================================
              //                       OTHER OPERATIONS
              // =============================================================
              /**
               * @dev Returns the message sender (defaults to `msg.sender`).
               *
               * If you are writing GSN compatible contracts, you need to override this function.
               */
              function _msgSenderERC721A() internal view virtual returns (address) {
                  return msg.sender;
              }
              /**
               * @dev Converts a uint256 to its ASCII string decimal representation.
               */
              function _toString(uint256 value) internal pure virtual returns (string memory str) {
                  assembly {
                      // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                      // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                      // We will need 1 word for the trailing zeros padding, 1 word for the length,
                      // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
                      let m := add(mload(0x40), 0xa0)
                      // Update the free memory pointer to allocate.
                      mstore(0x40, m)
                      // Assign the `str` to the end.
                      str := sub(m, 0x20)
                      // Zeroize the slot after the string.
                      mstore(str, 0)
                      // Cache the end of the memory to calculate the length later.
                      let end := str
                      // We write the string from rightmost digit to leftmost digit.
                      // The following is essentially a do-while loop that also handles the zero case.
                      // prettier-ignore
                      for { let temp := value } 1 {} {
                          str := sub(str, 1)
                          // Write the character to the pointer.
                          // The ASCII index of the '0' character is 48.
                          mstore8(str, add(48, mod(temp, 10)))
                          // Keep dividing `temp` until zero.
                          temp := div(temp, 10)
                          // prettier-ignore
                          if iszero(temp) { break }
                      }
                      let length := sub(end, str)
                      // Move the pointer 32 bytes leftwards to make room for the length.
                      str := sub(str, 0x20)
                      // Store the length.
                      mstore(str, length)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // ERC721A Contracts v4.2.2
          // Creator: Chiru Labs
          pragma solidity ^0.8.4;
          /**
           * @dev Interface of ERC721A.
           */
          interface IERC721A {
              /**
               * The caller must own the token or be an approved operator.
               */
              error ApprovalCallerNotOwnerNorApproved();
              /**
               * The token does not exist.
               */
              error ApprovalQueryForNonexistentToken();
              /**
               * Cannot query the balance for the zero address.
               */
              error BalanceQueryForZeroAddress();
              /**
               * Cannot mint to the zero address.
               */
              error MintToZeroAddress();
              /**
               * The quantity of tokens minted must be more than zero.
               */
              error MintZeroQuantity();
              /**
               * The token does not exist.
               */
              error OwnerQueryForNonexistentToken();
              /**
               * The caller must own the token or be an approved operator.
               */
              error TransferCallerNotOwnerNorApproved();
              /**
               * The token must be owned by `from`.
               */
              error TransferFromIncorrectOwner();
              /**
               * Cannot safely transfer to a contract that does not implement the
               * ERC721Receiver interface.
               */
              error TransferToNonERC721ReceiverImplementer();
              /**
               * Cannot transfer to the zero address.
               */
              error TransferToZeroAddress();
              /**
               * The token does not exist.
               */
              error URIQueryForNonexistentToken();
              /**
               * The `quantity` minted with ERC2309 exceeds the safety limit.
               */
              error MintERC2309QuantityExceedsLimit();
              /**
               * The `extraData` cannot be set on an unintialized ownership slot.
               */
              error OwnershipNotInitializedForExtraData();
              // =============================================================
              //                            STRUCTS
              // =============================================================
              struct TokenOwnership {
                  // The address of the owner.
                  address addr;
                  // Stores the start time of ownership with minimal overhead for tokenomics.
                  uint64 startTimestamp;
                  // Whether the token has been burned.
                  bool burned;
                  // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
                  uint24 extraData;
              }
              // =============================================================
              //                         TOKEN COUNTERS
              // =============================================================
              /**
               * @dev Returns the total number of tokens in existence.
               * Burned tokens will reduce the count.
               * To get the total number of tokens minted, please see {_totalMinted}.
               */
              function totalSupply() external view returns (uint256);
              // =============================================================
              //                            IERC165
              // =============================================================
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
              // =============================================================
              //                            IERC721
              // =============================================================
              /**
               * @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,
                  bytes calldata data
              ) external;
              /**
               * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) external;
              /**
               * @dev Transfers `tokenId` 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 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 the account approved for `tokenId` token.
               *
               * Requirements:
               *
               * - `tokenId` must exist.
               */
              function getApproved(uint256 tokenId) external view returns (address operator);
              /**
               * @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);
              // =============================================================
              //                        IERC721Metadata
              // =============================================================
              /**
               * @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);
              // =============================================================
              //                           IERC2309
              // =============================================================
              /**
               * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
               * (inclusive) is transferred from `from` to `to`, as defined in the
               * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
               *
               * See {_mintERC2309} for more details.
               */
              event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
          pragma solidity ^0.8.0;
          import "../utils/introspection/IERC165.sol";
          /**
           * @dev Interface for the NFT Royalty Standard.
           *
           * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
           * support for royalty payments across all NFT marketplaces and ecosystem participants.
           *
           * _Available since v4.5._
           */
          interface IERC2981 is IERC165 {
              /**
               * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
               * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
               */
              function royaltyInfo(uint256 tokenId, uint256 salePrice)
                  external
                  view
                  returns (address receiver, uint256 royaltyAmount);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface 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);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {OperatorFilterer} from "./OperatorFilterer.sol";
          import {CANONICAL_CORI_SUBSCRIPTION} from "./lib/Constants.sol";
          /**
           * @title  DefaultOperatorFilterer
           * @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
           * @dev    Please note that if your token contract does not provide an owner with EIP-173, it must provide
           *         administration methods on the contract itself to interact with the registry otherwise the subscription
           *         will be locked to the options set during construction.
           */
          abstract contract DefaultOperatorFilterer is OperatorFilterer {
              /// @dev The constructor that is called when the contract is being deployed.
              constructor() OperatorFilterer(CANONICAL_CORI_SUBSCRIPTION, true) {}
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          interface IOperatorFilterRegistry {
              /**
               * @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);
              /**
               * @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
               */
              function register(address registrant) external;
              /**
               * @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
               */
              function registerAndSubscribe(address registrant, address subscription) external;
              /**
               * @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;
              /**
               * @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 addr) external;
              /**
               * @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;
              /**
               * @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;
              /**
               * @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;
              /**
               * @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;
              /**
               * @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 registrantToSubscribe) external;
              /**
               * @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
               */
              function unsubscribe(address registrant, bool copyExistingEntries) external;
              /**
               * @notice Get the subscription address of a given registrant, if any.
               */
              function subscriptionOf(address addr) external returns (address registrant);
              /**
               * @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 returns (address[] memory);
              /**
               * @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 returns (address);
              /**
               * @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
               */
              function copyEntriesOf(address registrant, address registrantToCopy) external;
              /**
               * @notice Returns true if operator is filtered by a given address or its subscription.
               */
              function isOperatorFiltered(address registrant, address operator) external returns (bool);
              /**
               * @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 returns (bool);
              /**
               * @notice Returns true if a codeHash is filtered by a given address or its subscription.
               */
              function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
              /**
               * @notice Returns a list of filtered operators for a given address or its subscription.
               */
              function filteredOperators(address addr) external returns (address[] memory);
              /**
               * @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 addr) external returns (bytes32[] memory);
              /**
               * @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 returns (address);
              /**
               * @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 returns (bytes32);
              /**
               * @notice Returns true if an address has registered
               */
              function isRegistered(address addr) external returns (bool);
              /**
               * @dev Convenience method to compute the code hash of an arbitrary contract
               */
              function codeHashOf(address addr) external returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
          import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS} from "./lib/Constants.sol";
          /**
           * @title  OperatorFilterer
           * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
           *         registrant's entries in the OperatorFilterRegistry.
           * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
           *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
           *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
           *         Please note that if your token contract does not provide an owner with EIP-173, it must provide
           *         administration methods on the contract itself to interact with the registry otherwise the subscription
           *         will be locked to the options set during construction.
           */
          abstract contract OperatorFilterer {
              /// @dev Emitted when an operator is not allowed.
              error OperatorNotAllowed(address operator);
              IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
                  IOperatorFilterRegistry(CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS);
              /// @dev The constructor that is called when the contract is being deployed.
              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(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                      if (subscribe) {
                          OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                      } else {
                          if (subscriptionOrRegistrantToCopy != address(0)) {
                              OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                          } else {
                              OPERATOR_FILTER_REGISTRY.register(address(this));
                          }
                      }
                  }
              }
              /**
               * @dev A helper function to check if an operator is allowed.
               */
              modifier onlyAllowedOperator(address from) virtual {
                  // 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) {
                      _checkFilterOperator(msg.sender);
                  }
                  _;
              }
              /**
               * @dev A helper function to check if an operator approval is allowed.
               */
              modifier onlyAllowedOperatorApproval(address operator) virtual {
                  _checkFilterOperator(operator);
                  _;
              }
              /**
               * @dev A helper function to check if an operator is allowed.
               */
              function _checkFilterOperator(address operator) internal view virtual {
                  // Check registry code length to facilitate testing in environments without a deployed registry.
                  if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                      // under normal circumstances, this function will revert rather than return false, but inheriting contracts
                      // may specify their own OperatorFilterRegistry implementations, which may behave differently
                      if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                          revert OperatorNotAllowed(operator);
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          address constant CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS = 0x000000000000AAeB6D7670E522A718067333cd4E;
          address constant CANONICAL_CORI_SUBSCRIPTION = 0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6;
          // SPDX-License-Identifier: AGPL-3.0-only
          pragma solidity >=0.8.0;
          /// @notice Gas optimized reentrancy protection for smart contracts.
          /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
          /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
          abstract contract ReentrancyGuard {
              uint256 private locked = 1;
              modifier nonReentrant() virtual {
                  require(locked == 1, "REENTRANCY");
                  locked = 2;
                  _;
                  locked = 1;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.4;
          /**
           * @author emo.eth
           * @notice Abstract smart contract that provides an onlyUninitialized modifier which only allows calling when
           *         from within a constructor of some sort, whether directly instantiating an inherting contract,
           *         or when delegatecalling from a proxy
           */
          abstract contract ConstructorInitializable {
              error AlreadyInitialized();
              modifier onlyConstructor() {
                  if (address(this).code.length != 0) {
                      revert AlreadyInitialized();
                  }
                  _;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.4;
          import {ConstructorInitializable} from "./ConstructorInitializable.sol";
          /**
          @notice A two-step extension of Ownable, where the new owner must claim ownership of the contract after owner initiates transfer
          Owner can cancel the transfer at any point before the new owner claims ownership.
          Helpful in guarding against transferring ownership to an address that is unable to act as the Owner.
          */
          abstract contract TwoStepOwnable is ConstructorInitializable {
              address private _owner;
              event OwnershipTransferred(
                  address indexed previousOwner,
                  address indexed newOwner
              );
              address internal potentialOwner;
              event PotentialOwnerUpdated(address newPotentialAdministrator);
              error NewOwnerIsZeroAddress();
              error NotNextOwner();
              error OnlyOwner();
              modifier onlyOwner() {
                  _checkOwner();
                  _;
              }
              constructor() {
                  _initialize();
              }
              function _initialize() private onlyConstructor {
                  _transferOwnership(msg.sender);
              }
              ///@notice Initiate ownership transfer to newPotentialOwner. Note: new owner will have to manually acceptOwnership
              ///@param newPotentialOwner address of potential new owner
              function transferOwnership(address newPotentialOwner)
                  public
                  virtual
                  onlyOwner
              {
                  if (newPotentialOwner == address(0)) {
                      revert NewOwnerIsZeroAddress();
                  }
                  potentialOwner = newPotentialOwner;
                  emit PotentialOwnerUpdated(newPotentialOwner);
              }
              ///@notice Claim ownership of smart contract, after the current owner has initiated the process with transferOwnership
              function acceptOwnership() public virtual {
                  address _potentialOwner = potentialOwner;
                  if (msg.sender != _potentialOwner) {
                      revert NotNextOwner();
                  }
                  delete potentialOwner;
                  emit PotentialOwnerUpdated(address(0));
                  _transferOwnership(_potentialOwner);
              }
              ///@notice cancel ownership transfer
              function cancelOwnershipTransfer() public virtual onlyOwner {
                  delete potentialOwner;
                  emit PotentialOwnerUpdated(address(0));
              }
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if the sender is not the owner.
               */
              function _checkOwner() internal view virtual {
                  if (_owner != msg.sender) {
                      revert 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`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              ISeaDropTokenContractMetadata
          } from "./interfaces/ISeaDropTokenContractMetadata.sol";
          import { ERC721A } from "ERC721A/ERC721A.sol";
          import { TwoStepOwnable } from "utility-contracts/TwoStepOwnable.sol";
          import { IERC2981 } from "openzeppelin-contracts/interfaces/IERC2981.sol";
          import {
              IERC165
          } from "openzeppelin-contracts/utils/introspection/IERC165.sol";
          /**
           * @title  ERC721ContractMetadata
           * @author James Wenzel (emo.eth)
           * @author Ryan Ghods (ralxz.eth)
           * @author Stephan Min (stephanm.eth)
           * @notice ERC721ContractMetadata is a token contract that extends ERC721A
           *         with additional metadata and ownership capabilities.
           */
          contract ERC721ContractMetadata is
              ERC721A,
              TwoStepOwnable,
              ISeaDropTokenContractMetadata
          {
              /// @notice Track the max supply.
              uint256 _maxSupply;
              /// @notice Track the base URI for token metadata.
              string _tokenBaseURI;
              /// @notice Track the contract URI for contract metadata.
              string _contractURI;
              /// @notice Track the provenance hash for guaranteeing metadata order
              ///         for random reveals.
              bytes32 _provenanceHash;
              /// @notice Track the royalty info: address to receive royalties, and
              ///         royalty basis points.
              RoyaltyInfo _royaltyInfo;
              /**
               * @dev Reverts if the sender is not the owner or the contract itself.
               *      This function is inlined instead of being a modifier
               *      to save contract space from being inlined N times.
               */
              function _onlyOwnerOrSelf() internal view {
                  if (
                      _cast(msg.sender == owner()) | _cast(msg.sender == address(this)) ==
                      0
                  ) {
                      revert OnlyOwner();
                  }
              }
              /**
               * @notice Deploy the token contract with its name and symbol.
               */
              constructor(string memory name, string memory symbol)
                  ERC721A(name, symbol)
              {}
              /**
               * @notice Sets the base URI for the token metadata and emits an event.
               *
               * @param newBaseURI The new base URI to set.
               */
              function setBaseURI(string calldata newBaseURI) external override {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Set the new base URI.
                  _tokenBaseURI = newBaseURI;
                  // Emit an event with the update.
                  if (totalSupply() != 0) {
                      emit BatchMetadataUpdate(1, _nextTokenId() - 1);
                  }
              }
              /**
               * @notice Sets the contract URI for contract metadata.
               *
               * @param newContractURI The new contract URI.
               */
              function setContractURI(string calldata newContractURI) external override {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Set the new contract URI.
                  _contractURI = newContractURI;
                  // Emit an event with the update.
                  emit ContractURIUpdated(newContractURI);
              }
              /**
               * @notice Emit an event notifying metadata updates for
               *         a range of token ids, according to EIP-4906.
               *
               * @param fromTokenId The start token id.
               * @param toTokenId   The end token id.
               */
              function emitBatchMetadataUpdate(uint256 fromTokenId, uint256 toTokenId)
                  external
              {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Emit an event with the update.
                  emit BatchMetadataUpdate(fromTokenId, toTokenId);
              }
              /**
               * @notice Sets the max token supply and emits an event.
               *
               * @param newMaxSupply The new max supply to set.
               */
              function setMaxSupply(uint256 newMaxSupply) external {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the max supply does not exceed the maximum value of uint64.
                  if (newMaxSupply > 2**64 - 1) {
                      revert CannotExceedMaxSupplyOfUint64(newMaxSupply);
                  }
                  // Set the new max supply.
                  _maxSupply = newMaxSupply;
                  // Emit an event with the update.
                  emit MaxSupplyUpdated(newMaxSupply);
              }
              /**
               * @notice Sets the provenance hash and emits an event.
               *
               *         The provenance hash is used for random reveals, which
               *         is a hash of the ordered metadata to show it has not been
               *         modified after mint started.
               *
               *         This function will revert after the first item has been minted.
               *
               * @param newProvenanceHash The new provenance hash to set.
               */
              function setProvenanceHash(bytes32 newProvenanceHash) external {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Revert if any items have been minted.
                  if (_totalMinted() > 0) {
                      revert ProvenanceHashCannotBeSetAfterMintStarted();
                  }
                  // Keep track of the old provenance hash for emitting with the event.
                  bytes32 oldProvenanceHash = _provenanceHash;
                  // Set the new provenance hash.
                  _provenanceHash = newProvenanceHash;
                  // Emit an event with the update.
                  emit ProvenanceHashUpdated(oldProvenanceHash, newProvenanceHash);
              }
              /**
               * @notice Sets the address and basis points for royalties.
               *
               * @param newInfo The struct to configure royalties.
               */
              function setRoyaltyInfo(RoyaltyInfo calldata newInfo) external {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Revert if the new royalty address is the zero address.
                  if (newInfo.royaltyAddress == address(0)) {
                      revert RoyaltyAddressCannotBeZeroAddress();
                  }
                  // Revert if the new basis points is greater than 10_000.
                  if (newInfo.royaltyBps > 10_000) {
                      revert InvalidRoyaltyBasisPoints(newInfo.royaltyBps);
                  }
                  // Set the new royalty info.
                  _royaltyInfo = newInfo;
                  // Emit an event with the updated params.
                  emit RoyaltyInfoUpdated(newInfo.royaltyAddress, newInfo.royaltyBps);
              }
              /**
               * @notice Returns the base URI for token metadata.
               */
              function baseURI() external view override returns (string memory) {
                  return _baseURI();
              }
              /**
               * @notice Returns the base URI for the contract, which ERC721A uses
               *         to return tokenURI.
               */
              function _baseURI() internal view virtual override returns (string memory) {
                  return _tokenBaseURI;
              }
              /**
               * @notice Returns the contract URI for contract metadata.
               */
              function contractURI() external view override returns (string memory) {
                  return _contractURI;
              }
              /**
               * @notice Returns the max token supply.
               */
              function maxSupply() public view returns (uint256) {
                  return _maxSupply;
              }
              /**
               * @notice Returns the provenance hash.
               *         The provenance hash is used for random reveals, which
               *         is a hash of the ordered metadata to show it is unmodified
               *         after mint has started.
               */
              function provenanceHash() external view override returns (bytes32) {
                  return _provenanceHash;
              }
              /**
               * @notice Returns the address that receives royalties.
               */
              function royaltyAddress() external view returns (address) {
                  return _royaltyInfo.royaltyAddress;
              }
              /**
               * @notice Returns the royalty basis points out of 10_000.
               */
              function royaltyBasisPoints() external view returns (uint256) {
                  return _royaltyInfo.royaltyBps;
              }
              /**
               * @notice Called with the sale price to determine how much royalty
               *         is owed and to whom.
               *
               * @ param  _tokenId     The NFT asset queried for royalty information.
               * @param  _salePrice    The sale price of the NFT asset specified by
               *                       _tokenId.
               *
               * @return receiver      Address of who should be sent the royalty payment.
               * @return royaltyAmount The royalty payment amount for _salePrice.
               */
              function royaltyInfo(
                  uint256, /* _tokenId */
                  uint256 _salePrice
              ) external view returns (address receiver, uint256 royaltyAmount) {
                  // Put the royalty info on the stack for more efficient access.
                  RoyaltyInfo storage info = _royaltyInfo;
                  // Set the royalty amount to the sale price times the royalty basis
                  // points divided by 10_000.
                  royaltyAmount = (_salePrice * info.royaltyBps) / 10_000;
                  // Set the receiver of the royalty.
                  receiver = info.royaltyAddress;
              }
              /**
               * @notice Returns whether the interface is supported.
               *
               * @param interfaceId The interface id to check against.
               */
              function supportsInterface(bytes4 interfaceId)
                  public
                  view
                  virtual
                  override(IERC165, ERC721A)
                  returns (bool)
              {
                  return
                      interfaceId == type(IERC2981).interfaceId ||
                      interfaceId == 0x49064906 || // ERC-4906
                      super.supportsInterface(interfaceId);
              }
              /**
               * @dev Internal pure function to cast a `bool` value to a `uint256` value.
               *
               * @param b The `bool` value to cast.
               *
               * @return u The `uint256` value.
               */
              function _cast(bool b) internal pure returns (uint256 u) {
                  assembly {
                      u := b
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              ERC721ContractMetadata,
              ISeaDropTokenContractMetadata
          } from "./ERC721ContractMetadata.sol";
          import {
              INonFungibleSeaDropToken
          } from "./interfaces/INonFungibleSeaDropToken.sol";
          import { ISeaDrop } from "./interfaces/ISeaDrop.sol";
          import {
              AllowListData,
              PublicDrop,
              TokenGatedDropStage,
              SignedMintValidationParams
          } from "./lib/SeaDropStructs.sol";
          import {
              ERC721SeaDropStructsErrorsAndEvents
          } from "./lib/ERC721SeaDropStructsErrorsAndEvents.sol";
          import { ERC721A } from "ERC721A/ERC721A.sol";
          import { ReentrancyGuard } from "solmate/utils/ReentrancyGuard.sol";
          import {
              IERC165
          } from "openzeppelin-contracts/utils/introspection/IERC165.sol";
          import {
              DefaultOperatorFilterer
          } from "operator-filter-registry/DefaultOperatorFilterer.sol";
          /**
           * @title  ERC721SeaDrop
           * @author James Wenzel (emo.eth)
           * @author Ryan Ghods (ralxz.eth)
           * @author Stephan Min (stephanm.eth)
           * @author Michael Cohen (notmichael.eth)
           * @notice ERC721SeaDrop is a token contract that contains methods
           *         to properly interact with SeaDrop.
           */
          contract ERC721SeaDrop is
              ERC721ContractMetadata,
              INonFungibleSeaDropToken,
              ERC721SeaDropStructsErrorsAndEvents,
              ReentrancyGuard,
              DefaultOperatorFilterer
          {
              /// @notice Track the allowed SeaDrop addresses.
              mapping(address => bool) internal _allowedSeaDrop;
              /// @notice Track the enumerated allowed SeaDrop addresses.
              address[] internal _enumeratedAllowedSeaDrop;
              /**
               * @dev Reverts if not an allowed SeaDrop contract.
               *      This function is inlined instead of being a modifier
               *      to save contract space from being inlined N times.
               *
               * @param seaDrop The SeaDrop address to check if allowed.
               */
              function _onlyAllowedSeaDrop(address seaDrop) internal view {
                  if (_allowedSeaDrop[seaDrop] != true) {
                      revert OnlyAllowedSeaDrop();
                  }
              }
              /**
               * @notice Deploy the token contract with its name, symbol,
               *         and allowed SeaDrop addresses.
               */
              constructor(
                  string memory name,
                  string memory symbol,
                  address[] memory allowedSeaDrop
              ) ERC721ContractMetadata(name, symbol) {
                  // Put the length on the stack for more efficient access.
                  uint256 allowedSeaDropLength = allowedSeaDrop.length;
                  // Set the mapping for allowed SeaDrop contracts.
                  for (uint256 i = 0; i < allowedSeaDropLength; ) {
                      _allowedSeaDrop[allowedSeaDrop[i]] = true;
                      unchecked {
                          ++i;
                      }
                  }
                  // Set the enumeration.
                  _enumeratedAllowedSeaDrop = allowedSeaDrop;
                  // Emit an event noting the contract deployment.
                  emit SeaDropTokenDeployed();
              }
              /**
               * @notice Update the allowed SeaDrop contracts.
               *         Only the owner or administrator can use this function.
               *
               * @param allowedSeaDrop The allowed SeaDrop addresses.
               */
              function updateAllowedSeaDrop(address[] calldata allowedSeaDrop)
                  external
                  virtual
                  override
                  onlyOwner
              {
                  _updateAllowedSeaDrop(allowedSeaDrop);
              }
              /**
               * @notice Internal function to update the allowed SeaDrop contracts.
               *
               * @param allowedSeaDrop The allowed SeaDrop addresses.
               */
              function _updateAllowedSeaDrop(address[] calldata allowedSeaDrop) internal {
                  // Put the length on the stack for more efficient access.
                  uint256 enumeratedAllowedSeaDropLength = _enumeratedAllowedSeaDrop
                      .length;
                  uint256 allowedSeaDropLength = allowedSeaDrop.length;
                  // Reset the old mapping.
                  for (uint256 i = 0; i < enumeratedAllowedSeaDropLength; ) {
                      _allowedSeaDrop[_enumeratedAllowedSeaDrop[i]] = false;
                      unchecked {
                          ++i;
                      }
                  }
                  // Set the new mapping for allowed SeaDrop contracts.
                  for (uint256 i = 0; i < allowedSeaDropLength; ) {
                      _allowedSeaDrop[allowedSeaDrop[i]] = true;
                      unchecked {
                          ++i;
                      }
                  }
                  // Set the enumeration.
                  _enumeratedAllowedSeaDrop = allowedSeaDrop;
                  // Emit an event for the update.
                  emit AllowedSeaDropUpdated(allowedSeaDrop);
              }
              /**
               * @dev Overrides the `_startTokenId` function from ERC721A
               *      to start at token id `1`.
               *
               *      This is to avoid future possible problems since `0` is usually
               *      used to signal values that have not been set or have been removed.
               */
              function _startTokenId() internal view virtual override returns (uint256) {
                  return 1;
              }
              /**
               * @dev Overrides the `tokenURI()` function from ERC721A
               *      to return just the base URI if it is implied to not be a directory.
               *
               *      This is to help with ERC721 contracts in which the same token URI
               *      is desired for each token, such as when the tokenURI is 'unrevealed'.
               */
              function tokenURI(uint256 tokenId)
                  public
                  view
                  virtual
                  override
                  returns (string memory)
              {
                  if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
                  string memory baseURI = _baseURI();
                  // Exit early if the baseURI is empty.
                  if (bytes(baseURI).length == 0) {
                      return "";
                  }
                  // Check if the last character in baseURI is a slash.
                  if (bytes(baseURI)[bytes(baseURI).length - 1] != bytes("/")[0]) {
                      return baseURI;
                  }
                  return string(abi.encodePacked(baseURI, _toString(tokenId)));
              }
              /**
               * @notice Mint tokens, restricted to the SeaDrop contract.
               *
               * @dev    NOTE: If a token registers itself with multiple SeaDrop
               *         contracts, the implementation of this function should guard
               *         against reentrancy. If the implementing token uses
               *         _safeMint(), or a feeRecipient with a malicious receive() hook
               *         is specified, the token or fee recipients may be able to execute
               *         another mint in the same transaction via a separate SeaDrop
               *         contract.
               *         This is dangerous if an implementing token does not correctly
               *         update the minterNumMinted and currentTotalSupply values before
               *         transferring minted tokens, as SeaDrop references these values
               *         to enforce token limits on a per-wallet and per-stage basis.
               *
               *         ERC721A tracks these values automatically, but this note and
               *         nonReentrant modifier are left here to encourage best-practices
               *         when referencing this contract.
               *
               * @param minter   The address to mint to.
               * @param quantity The number of tokens to mint.
               */
              function mintSeaDrop(address minter, uint256 quantity)
                  external
                  virtual
                  override
                  nonReentrant
              {
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(msg.sender);
                  // Extra safety check to ensure the max supply is not exceeded.
                  if (_totalMinted() + quantity > maxSupply()) {
                      revert MintQuantityExceedsMaxSupply(
                          _totalMinted() + quantity,
                          maxSupply()
                      );
                  }
                  // Mint the quantity of tokens to the minter.
                  _safeMint(minter, quantity);
              }
              /**
               * @notice Update the public drop data for this nft contract on SeaDrop.
               *         Only the owner can use this function.
               *
               * @param seaDropImpl The allowed SeaDrop contract.
               * @param publicDrop  The public drop data.
               */
              function updatePublicDrop(
                  address seaDropImpl,
                  PublicDrop calldata publicDrop
              ) external virtual override {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(seaDropImpl);
                  // Update the public drop data on SeaDrop.
                  ISeaDrop(seaDropImpl).updatePublicDrop(publicDrop);
              }
              /**
               * @notice Update the allow list data for this nft contract on SeaDrop.
               *         Only the owner can use this function.
               *
               * @param seaDropImpl   The allowed SeaDrop contract.
               * @param allowListData The allow list data.
               */
              function updateAllowList(
                  address seaDropImpl,
                  AllowListData calldata allowListData
              ) external virtual override {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(seaDropImpl);
                  // Update the allow list on SeaDrop.
                  ISeaDrop(seaDropImpl).updateAllowList(allowListData);
              }
              /**
               * @notice Update the token gated drop stage data for this nft contract
               *         on SeaDrop.
               *         Only the owner can use this function.
               *
               *         Note: If two INonFungibleSeaDropToken tokens are doing
               *         simultaneous token gated drop promotions for each other,
               *         they can be minted by the same actor until
               *         `maxTokenSupplyForStage` is reached. Please ensure the
               *         `allowedNftToken` is not running an active drop during the
               *         `dropStage` time period.
               *
               * @param seaDropImpl     The allowed SeaDrop contract.
               * @param allowedNftToken The allowed nft token.
               * @param dropStage       The token gated drop stage data.
               */
              function updateTokenGatedDrop(
                  address seaDropImpl,
                  address allowedNftToken,
                  TokenGatedDropStage calldata dropStage
              ) external virtual override {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(seaDropImpl);
                  // Update the token gated drop stage.
                  ISeaDrop(seaDropImpl).updateTokenGatedDrop(allowedNftToken, dropStage);
              }
              /**
               * @notice Update the drop URI for this nft contract on SeaDrop.
               *         Only the owner can use this function.
               *
               * @param seaDropImpl The allowed SeaDrop contract.
               * @param dropURI     The new drop URI.
               */
              function updateDropURI(address seaDropImpl, string calldata dropURI)
                  external
                  virtual
                  override
              {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(seaDropImpl);
                  // Update the drop URI.
                  ISeaDrop(seaDropImpl).updateDropURI(dropURI);
              }
              /**
               * @notice Update the creator payout address for this nft contract on
               *         SeaDrop.
               *         Only the owner can set the creator payout address.
               *
               * @param seaDropImpl   The allowed SeaDrop contract.
               * @param payoutAddress The new payout address.
               */
              function updateCreatorPayoutAddress(
                  address seaDropImpl,
                  address payoutAddress
              ) external {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(seaDropImpl);
                  // Update the creator payout address.
                  ISeaDrop(seaDropImpl).updateCreatorPayoutAddress(payoutAddress);
              }
              /**
               * @notice Update the allowed fee recipient for this nft contract
               *         on SeaDrop.
               *         Only the owner can set the allowed fee recipient.
               *
               * @param seaDropImpl  The allowed SeaDrop contract.
               * @param feeRecipient The new fee recipient.
               * @param allowed      If the fee recipient is allowed.
               */
              function updateAllowedFeeRecipient(
                  address seaDropImpl,
                  address feeRecipient,
                  bool allowed
              ) external virtual {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(seaDropImpl);
                  // Update the allowed fee recipient.
                  ISeaDrop(seaDropImpl).updateAllowedFeeRecipient(feeRecipient, allowed);
              }
              /**
               * @notice Update the server-side signers for this nft contract
               *         on SeaDrop.
               *         Only the owner can use this function.
               *
               * @param seaDropImpl                The allowed SeaDrop contract.
               * @param signer                     The signer to update.
               * @param signedMintValidationParams Minimum and maximum parameters to
               *                                   enforce for signed mints.
               */
              function updateSignedMintValidationParams(
                  address seaDropImpl,
                  address signer,
                  SignedMintValidationParams memory signedMintValidationParams
              ) external virtual override {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(seaDropImpl);
                  // Update the signer.
                  ISeaDrop(seaDropImpl).updateSignedMintValidationParams(
                      signer,
                      signedMintValidationParams
                  );
              }
              /**
               * @notice Update the allowed payers for this nft contract on SeaDrop.
               *         Only the owner can use this function.
               *
               * @param seaDropImpl The allowed SeaDrop contract.
               * @param payer       The payer to update.
               * @param allowed     Whether the payer is allowed.
               */
              function updatePayer(
                  address seaDropImpl,
                  address payer,
                  bool allowed
              ) external virtual override {
                  // Ensure the sender is only the owner or contract itself.
                  _onlyOwnerOrSelf();
                  // Ensure the SeaDrop is allowed.
                  _onlyAllowedSeaDrop(seaDropImpl);
                  // Update the payer.
                  ISeaDrop(seaDropImpl).updatePayer(payer, allowed);
              }
              /**
               * @notice Returns a set of mint stats for the address.
               *         This assists SeaDrop in enforcing maxSupply,
               *         maxTotalMintableByWallet, and maxTokenSupplyForStage checks.
               *
               * @dev    NOTE: Implementing contracts should always update these numbers
               *         before transferring any tokens with _safeMint() to mitigate
               *         consequences of malicious onERC721Received() hooks.
               *
               * @param minter The minter address.
               */
              function getMintStats(address minter)
                  external
                  view
                  override
                  returns (
                      uint256 minterNumMinted,
                      uint256 currentTotalSupply,
                      uint256 maxSupply
                  )
              {
                  minterNumMinted = _numberMinted(minter);
                  currentTotalSupply = _totalMinted();
                  maxSupply = _maxSupply;
              }
              /**
               * @notice Returns whether the interface is supported.
               *
               * @param interfaceId The interface id to check against.
               */
              function supportsInterface(bytes4 interfaceId)
                  public
                  view
                  virtual
                  override(IERC165, ERC721ContractMetadata)
                  returns (bool)
              {
                  return
                      interfaceId == type(INonFungibleSeaDropToken).interfaceId ||
                      interfaceId == type(ISeaDropTokenContractMetadata).interfaceId ||
                      // ERC721ContractMetadata returns supportsInterface true for
                      //     EIP-2981
                      // ERC721A returns supportsInterface true for
                      //     ERC165, ERC721, ERC721Metadata
                      super.supportsInterface(interfaceId);
              }
              /**
               * @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.
               * - The `operator` must be allowed.
               *
               * Emits an {ApprovalForAll} event.
               */
              function setApprovalForAll(address operator, bool approved)
                  public
                  override
                  onlyAllowedOperatorApproval(operator)
              {
                  super.setApprovalForAll(operator, approved);
              }
              /**
               * @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.
               * - The `operator` mut be allowed.
               *
               * Emits an {Approval} event.
               */
              function approve(address operator, uint256 tokenId)
                  public
                  override
                  onlyAllowedOperatorApproval(operator)
              {
                  super.approve(operator, tokenId);
              }
              /**
               * @dev Transfers `tokenId` from `from` to `to`.
               *
               * 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}.
               * - The operator must be allowed.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) public override onlyAllowedOperator(from) {
                  super.transferFrom(from, to, tokenId);
              }
              /**
               * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) public override onlyAllowedOperator(from) {
                  super.safeTransferFrom(from, to, tokenId);
              }
              /**
               * @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.
               * - The operator must be allowed.
               *
               * Emits a {Transfer} event.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory data
              ) public override onlyAllowedOperator(from) {
                  super.safeTransferFrom(from, to, tokenId, data);
              }
              /**
               * @notice Configure multiple properties at a time.
               *
               *         Note: The individual configure methods should be used
               *         to unset or reset any properties to zero, as this method
               *         will ignore zero-value properties in the config struct.
               *
               * @param config The configuration struct.
               */
              function multiConfigure(MultiConfigureStruct calldata config)
                  external
                  onlyOwner
              {
                  if (config.maxSupply > 0) {
                      this.setMaxSupply(config.maxSupply);
                  }
                  if (bytes(config.baseURI).length != 0) {
                      this.setBaseURI(config.baseURI);
                  }
                  if (bytes(config.contractURI).length != 0) {
                      this.setContractURI(config.contractURI);
                  }
                  if (
                      _cast(config.publicDrop.startTime != 0) |
                          _cast(config.publicDrop.endTime != 0) ==
                      1
                  ) {
                      this.updatePublicDrop(config.seaDropImpl, config.publicDrop);
                  }
                  if (bytes(config.dropURI).length != 0) {
                      this.updateDropURI(config.seaDropImpl, config.dropURI);
                  }
                  if (config.allowListData.merkleRoot != bytes32(0)) {
                      this.updateAllowList(config.seaDropImpl, config.allowListData);
                  }
                  if (config.creatorPayoutAddress != address(0)) {
                      this.updateCreatorPayoutAddress(
                          config.seaDropImpl,
                          config.creatorPayoutAddress
                      );
                  }
                  if (config.provenanceHash != bytes32(0)) {
                      this.setProvenanceHash(config.provenanceHash);
                  }
                  if (config.allowedFeeRecipients.length > 0) {
                      for (uint256 i = 0; i < config.allowedFeeRecipients.length; ) {
                          this.updateAllowedFeeRecipient(
                              config.seaDropImpl,
                              config.allowedFeeRecipients[i],
                              true
                          );
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  if (config.disallowedFeeRecipients.length > 0) {
                      for (uint256 i = 0; i < config.disallowedFeeRecipients.length; ) {
                          this.updateAllowedFeeRecipient(
                              config.seaDropImpl,
                              config.disallowedFeeRecipients[i],
                              false
                          );
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  if (config.allowedPayers.length > 0) {
                      for (uint256 i = 0; i < config.allowedPayers.length; ) {
                          this.updatePayer(
                              config.seaDropImpl,
                              config.allowedPayers[i],
                              true
                          );
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  if (config.disallowedPayers.length > 0) {
                      for (uint256 i = 0; i < config.disallowedPayers.length; ) {
                          this.updatePayer(
                              config.seaDropImpl,
                              config.disallowedPayers[i],
                              false
                          );
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  if (config.tokenGatedDropStages.length > 0) {
                      if (
                          config.tokenGatedDropStages.length !=
                          config.tokenGatedAllowedNftTokens.length
                      ) {
                          revert TokenGatedMismatch();
                      }
                      for (uint256 i = 0; i < config.tokenGatedDropStages.length; ) {
                          this.updateTokenGatedDrop(
                              config.seaDropImpl,
                              config.tokenGatedAllowedNftTokens[i],
                              config.tokenGatedDropStages[i]
                          );
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  if (config.disallowedTokenGatedAllowedNftTokens.length > 0) {
                      for (
                          uint256 i = 0;
                          i < config.disallowedTokenGatedAllowedNftTokens.length;
                      ) {
                          TokenGatedDropStage memory emptyStage;
                          this.updateTokenGatedDrop(
                              config.seaDropImpl,
                              config.disallowedTokenGatedAllowedNftTokens[i],
                              emptyStage
                          );
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  if (config.signedMintValidationParams.length > 0) {
                      if (
                          config.signedMintValidationParams.length !=
                          config.signers.length
                      ) {
                          revert SignersMismatch();
                      }
                      for (
                          uint256 i = 0;
                          i < config.signedMintValidationParams.length;
                      ) {
                          this.updateSignedMintValidationParams(
                              config.seaDropImpl,
                              config.signers[i],
                              config.signedMintValidationParams[i]
                          );
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  if (config.disallowedSigners.length > 0) {
                      for (uint256 i = 0; i < config.disallowedSigners.length; ) {
                          SignedMintValidationParams memory emptyParams;
                          this.updateSignedMintValidationParams(
                              config.seaDropImpl,
                              config.disallowedSigners[i],
                              emptyParams
                          );
                          unchecked {
                              ++i;
                          }
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              ISeaDropTokenContractMetadata
          } from "./ISeaDropTokenContractMetadata.sol";
          import {
              AllowListData,
              PublicDrop,
              TokenGatedDropStage,
              SignedMintValidationParams
          } from "../lib/SeaDropStructs.sol";
          interface INonFungibleSeaDropToken is ISeaDropTokenContractMetadata {
              /**
               * @dev Revert with an error if a contract is not an allowed
               *      SeaDrop address.
               */
              error OnlyAllowedSeaDrop();
              /**
               * @dev Emit an event when allowed SeaDrop contracts are updated.
               */
              event AllowedSeaDropUpdated(address[] allowedSeaDrop);
              /**
               * @notice Update the allowed SeaDrop contracts.
               *         Only the owner or administrator can use this function.
               *
               * @param allowedSeaDrop The allowed SeaDrop addresses.
               */
              function updateAllowedSeaDrop(address[] calldata allowedSeaDrop) external;
              /**
               * @notice Mint tokens, restricted to the SeaDrop contract.
               *
               * @dev    NOTE: If a token registers itself with multiple SeaDrop
               *         contracts, the implementation of this function should guard
               *         against reentrancy. If the implementing token uses
               *         _safeMint(), or a feeRecipient with a malicious receive() hook
               *         is specified, the token or fee recipients may be able to execute
               *         another mint in the same transaction via a separate SeaDrop
               *         contract.
               *         This is dangerous if an implementing token does not correctly
               *         update the minterNumMinted and currentTotalSupply values before
               *         transferring minted tokens, as SeaDrop references these values
               *         to enforce token limits on a per-wallet and per-stage basis.
               *
               * @param minter   The address to mint to.
               * @param quantity The number of tokens to mint.
               */
              function mintSeaDrop(address minter, uint256 quantity) external;
              /**
               * @notice Returns a set of mint stats for the address.
               *         This assists SeaDrop in enforcing maxSupply,
               *         maxTotalMintableByWallet, and maxTokenSupplyForStage checks.
               *
               * @dev    NOTE: Implementing contracts should always update these numbers
               *         before transferring any tokens with _safeMint() to mitigate
               *         consequences of malicious onERC721Received() hooks.
               *
               * @param minter The minter address.
               */
              function getMintStats(address minter)
                  external
                  view
                  returns (
                      uint256 minterNumMinted,
                      uint256 currentTotalSupply,
                      uint256 maxSupply
                  );
              /**
               * @notice Update the public drop data for this nft contract on SeaDrop.
               *         Only the owner or administrator can use this function.
               *
               *         The administrator can only update `feeBps`.
               *
               * @param seaDropImpl The allowed SeaDrop contract.
               * @param publicDrop  The public drop data.
               */
              function updatePublicDrop(
                  address seaDropImpl,
                  PublicDrop calldata publicDrop
              ) external;
              /**
               * @notice Update the allow list data for this nft contract on SeaDrop.
               *         Only the owner or administrator can use this function.
               *
               * @param seaDropImpl   The allowed SeaDrop contract.
               * @param allowListData The allow list data.
               */
              function updateAllowList(
                  address seaDropImpl,
                  AllowListData calldata allowListData
              ) external;
              /**
               * @notice Update the token gated drop stage data for this nft contract
               *         on SeaDrop.
               *         Only the owner or administrator can use this function.
               *
               *         The administrator, when present, must first set `feeBps`.
               *
               *         Note: If two INonFungibleSeaDropToken tokens are doing
               *         simultaneous token gated drop promotions for each other,
               *         they can be minted by the same actor until
               *         `maxTokenSupplyForStage` is reached. Please ensure the
               *         `allowedNftToken` is not running an active drop during the
               *         `dropStage` time period.
               *
               *
               * @param seaDropImpl     The allowed SeaDrop contract.
               * @param allowedNftToken The allowed nft token.
               * @param dropStage       The token gated drop stage data.
               */
              function updateTokenGatedDrop(
                  address seaDropImpl,
                  address allowedNftToken,
                  TokenGatedDropStage calldata dropStage
              ) external;
              /**
               * @notice Update the drop URI for this nft contract on SeaDrop.
               *         Only the owner or administrator can use this function.
               *
               * @param seaDropImpl The allowed SeaDrop contract.
               * @param dropURI     The new drop URI.
               */
              function updateDropURI(address seaDropImpl, string calldata dropURI)
                  external;
              /**
               * @notice Update the creator payout address for this nft contract on
               *         SeaDrop.
               *         Only the owner can set the creator payout address.
               *
               * @param seaDropImpl   The allowed SeaDrop contract.
               * @param payoutAddress The new payout address.
               */
              function updateCreatorPayoutAddress(
                  address seaDropImpl,
                  address payoutAddress
              ) external;
              /**
               * @notice Update the allowed fee recipient for this nft contract
               *         on SeaDrop.
               *         Only the administrator can set the allowed fee recipient.
               *
               * @param seaDropImpl  The allowed SeaDrop contract.
               * @param feeRecipient The new fee recipient.
               */
              function updateAllowedFeeRecipient(
                  address seaDropImpl,
                  address feeRecipient,
                  bool allowed
              ) external;
              /**
               * @notice Update the server-side signers for this nft contract
               *         on SeaDrop.
               *         Only the owner or administrator can use this function.
               *
               * @param seaDropImpl                The allowed SeaDrop contract.
               * @param signer                     The signer to update.
               * @param signedMintValidationParams Minimum and maximum parameters
               *                                   to enforce for signed mints.
               */
              function updateSignedMintValidationParams(
                  address seaDropImpl,
                  address signer,
                  SignedMintValidationParams memory signedMintValidationParams
              ) external;
              /**
               * @notice Update the allowed payers for this nft contract on SeaDrop.
               *         Only the owner or administrator can use this function.
               *
               * @param seaDropImpl The allowed SeaDrop contract.
               * @param payer       The payer to update.
               * @param allowed     Whether the payer is allowed.
               */
              function updatePayer(
                  address seaDropImpl,
                  address payer,
                  bool allowed
              ) external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              AllowListData,
              MintParams,
              PublicDrop,
              TokenGatedDropStage,
              TokenGatedMintParams,
              SignedMintValidationParams
          } from "../lib/SeaDropStructs.sol";
          import { SeaDropErrorsAndEvents } from "../lib/SeaDropErrorsAndEvents.sol";
          interface ISeaDrop is SeaDropErrorsAndEvents {
              /**
               * @notice Mint a public drop.
               *
               * @param nftContract      The nft contract to mint.
               * @param feeRecipient     The fee recipient.
               * @param minterIfNotPayer The mint recipient if different than the payer.
               * @param quantity         The number of tokens to mint.
               */
              function mintPublic(
                  address nftContract,
                  address feeRecipient,
                  address minterIfNotPayer,
                  uint256 quantity
              ) external payable;
              /**
               * @notice Mint from an allow list.
               *
               * @param nftContract      The nft contract to mint.
               * @param feeRecipient     The fee recipient.
               * @param minterIfNotPayer The mint recipient if different than the payer.
               * @param quantity         The number of tokens to mint.
               * @param mintParams       The mint parameters.
               * @param proof            The proof for the leaf of the allow list.
               */
              function mintAllowList(
                  address nftContract,
                  address feeRecipient,
                  address minterIfNotPayer,
                  uint256 quantity,
                  MintParams calldata mintParams,
                  bytes32[] calldata proof
              ) external payable;
              /**
               * @notice Mint with a server-side signature.
               *         Note that a signature can only be used once.
               *
               * @param nftContract      The nft contract to mint.
               * @param feeRecipient     The fee recipient.
               * @param minterIfNotPayer The mint recipient if different than the payer.
               * @param quantity         The number of tokens to mint.
               * @param mintParams       The mint parameters.
               * @param salt             The sale for the signed mint.
               * @param signature        The server-side signature, must be an allowed
               *                         signer.
               */
              function mintSigned(
                  address nftContract,
                  address feeRecipient,
                  address minterIfNotPayer,
                  uint256 quantity,
                  MintParams calldata mintParams,
                  uint256 salt,
                  bytes calldata signature
              ) external payable;
              /**
               * @notice Mint as an allowed token holder.
               *         This will mark the token id as redeemed and will revert if the
               *         same token id is attempted to be redeemed twice.
               *
               * @param nftContract      The nft contract to mint.
               * @param feeRecipient     The fee recipient.
               * @param minterIfNotPayer The mint recipient if different than the payer.
               * @param mintParams       The token gated mint params.
               */
              function mintAllowedTokenHolder(
                  address nftContract,
                  address feeRecipient,
                  address minterIfNotPayer,
                  TokenGatedMintParams calldata mintParams
              ) external payable;
              /**
               * @notice Emits an event to notify update of the drop URI.
               *
               *         This method assume msg.sender is an nft contract and its
               *         ERC165 interface id matches INonFungibleSeaDropToken.
               *
               *         Note: Be sure only authorized users can call this from
               *         token contracts that implement INonFungibleSeaDropToken.
               *
               * @param dropURI The new drop URI.
               */
              function updateDropURI(string calldata dropURI) external;
              /**
               * @notice Updates the public drop data for the nft contract
               *         and emits an event.
               *
               *         This method assume msg.sender is an nft contract and its
               *         ERC165 interface id matches INonFungibleSeaDropToken.
               *
               *         Note: Be sure only authorized users can call this from
               *         token contracts that implement INonFungibleSeaDropToken.
               *
               * @param publicDrop The public drop data.
               */
              function updatePublicDrop(PublicDrop calldata publicDrop) external;
              /**
               * @notice Updates the allow list merkle root for the nft contract
               *         and emits an event.
               *
               *         This method assume msg.sender is an nft contract and its
               *         ERC165 interface id matches INonFungibleSeaDropToken.
               *
               *         Note: Be sure only authorized users can call this from
               *         token contracts that implement INonFungibleSeaDropToken.
               *
               * @param allowListData The allow list data.
               */
              function updateAllowList(AllowListData calldata allowListData) external;
              /**
               * @notice Updates the token gated drop stage for the nft contract
               *         and emits an event.
               *
               *         This method assume msg.sender is an nft contract and its
               *         ERC165 interface id matches INonFungibleSeaDropToken.
               *
               *         Note: Be sure only authorized users can call this from
               *         token contracts that implement INonFungibleSeaDropToken.
               *
               *         Note: If two INonFungibleSeaDropToken tokens are doing
               *         simultaneous token gated drop promotions for each other,
               *         they can be minted by the same actor until
               *         `maxTokenSupplyForStage` is reached. Please ensure the
               *         `allowedNftToken` is not running an active drop during
               *         the `dropStage` time period.
               *
               * @param allowedNftToken The token gated nft token.
               * @param dropStage       The token gated drop stage data.
               */
              function updateTokenGatedDrop(
                  address allowedNftToken,
                  TokenGatedDropStage calldata dropStage
              ) external;
              /**
               * @notice Updates the creator payout address and emits an event.
               *
               *         This method assume msg.sender is an nft contract and its
               *         ERC165 interface id matches INonFungibleSeaDropToken.
               *
               *         Note: Be sure only authorized users can call this from
               *         token contracts that implement INonFungibleSeaDropToken.
               *
               * @param payoutAddress The creator payout address.
               */
              function updateCreatorPayoutAddress(address payoutAddress) external;
              /**
               * @notice Updates the allowed fee recipient and emits an event.
               *
               *         This method assume msg.sender is an nft contract and its
               *         ERC165 interface id matches INonFungibleSeaDropToken.
               *
               *         Note: Be sure only authorized users can call this from
               *         token contracts that implement INonFungibleSeaDropToken.
               *
               * @param feeRecipient The fee recipient.
               * @param allowed      If the fee recipient is allowed.
               */
              function updateAllowedFeeRecipient(address feeRecipient, bool allowed)
                  external;
              /**
               * @notice Updates the allowed server-side signers and emits an event.
               *
               *         This method assume msg.sender is an nft contract and its
               *         ERC165 interface id matches INonFungibleSeaDropToken.
               *
               *         Note: Be sure only authorized users can call this from
               *         token contracts that implement INonFungibleSeaDropToken.
               *
               * @param signer                     The signer to update.
               * @param signedMintValidationParams Minimum and maximum parameters
               *                                   to enforce for signed mints.
               */
              function updateSignedMintValidationParams(
                  address signer,
                  SignedMintValidationParams calldata signedMintValidationParams
              ) external;
              /**
               * @notice Updates the allowed payer and emits an event.
               *
               *         This method assume msg.sender is an nft contract and its
               *         ERC165 interface id matches INonFungibleSeaDropToken.
               *
               *         Note: Be sure only authorized users can call this from
               *         token contracts that implement INonFungibleSeaDropToken.
               *
               * @param payer   The payer to add or remove.
               * @param allowed Whether to add or remove the payer.
               */
              function updatePayer(address payer, bool allowed) external;
              /**
               * @notice Returns the public drop data for the nft contract.
               *
               * @param nftContract The nft contract.
               */
              function getPublicDrop(address nftContract)
                  external
                  view
                  returns (PublicDrop memory);
              /**
               * @notice Returns the creator payout address for the nft contract.
               *
               * @param nftContract The nft contract.
               */
              function getCreatorPayoutAddress(address nftContract)
                  external
                  view
                  returns (address);
              /**
               * @notice Returns the allow list merkle root for the nft contract.
               *
               * @param nftContract The nft contract.
               */
              function getAllowListMerkleRoot(address nftContract)
                  external
                  view
                  returns (bytes32);
              /**
               * @notice Returns if the specified fee recipient is allowed
               *         for the nft contract.
               *
               * @param nftContract  The nft contract.
               * @param feeRecipient The fee recipient.
               */
              function getFeeRecipientIsAllowed(address nftContract, address feeRecipient)
                  external
                  view
                  returns (bool);
              /**
               * @notice Returns an enumeration of allowed fee recipients for an
               *         nft contract when fee recipients are enforced
               *
               * @param nftContract The nft contract.
               */
              function getAllowedFeeRecipients(address nftContract)
                  external
                  view
                  returns (address[] memory);
              /**
               * @notice Returns the server-side signers for the nft contract.
               *
               * @param nftContract The nft contract.
               */
              function getSigners(address nftContract)
                  external
                  view
                  returns (address[] memory);
              /**
               * @notice Returns the struct of SignedMintValidationParams for a signer.
               *
               * @param nftContract The nft contract.
               * @param signer      The signer.
               */
              function getSignedMintValidationParams(address nftContract, address signer)
                  external
                  view
                  returns (SignedMintValidationParams memory);
              /**
               * @notice Returns the payers for the nft contract.
               *
               * @param nftContract The nft contract.
               */
              function getPayers(address nftContract)
                  external
                  view
                  returns (address[] memory);
              /**
               * @notice Returns if the specified payer is allowed
               *         for the nft contract.
               *
               * @param nftContract The nft contract.
               * @param payer       The payer.
               */
              function getPayerIsAllowed(address nftContract, address payer)
                  external
                  view
                  returns (bool);
              /**
               * @notice Returns the allowed token gated drop tokens for the nft contract.
               *
               * @param nftContract The nft contract.
               */
              function getTokenGatedAllowedTokens(address nftContract)
                  external
                  view
                  returns (address[] memory);
              /**
               * @notice Returns the token gated drop data for the nft contract
               *         and token gated nft.
               *
               * @param nftContract     The nft contract.
               * @param allowedNftToken The token gated nft token.
               */
              function getTokenGatedDrop(address nftContract, address allowedNftToken)
                  external
                  view
                  returns (TokenGatedDropStage memory);
              /**
               * @notice Returns whether the token id for a token gated drop has been
               *         redeemed.
               *
               * @param nftContract       The nft contract.
               * @param allowedNftToken   The token gated nft token.
               * @param allowedNftTokenId The token gated nft token id to check.
               */
              function getAllowedNftTokenIdIsRedeemed(
                  address nftContract,
                  address allowedNftToken,
                  uint256 allowedNftTokenId
              ) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { IERC2981 } from "openzeppelin-contracts/interfaces/IERC2981.sol";
          interface ISeaDropTokenContractMetadata is IERC2981 {
              /**
               * @notice Throw if the max supply exceeds uint64, a limit
               *         due to the storage of bit-packed variables in ERC721A.
               */
              error CannotExceedMaxSupplyOfUint64(uint256 newMaxSupply);
              /**
               * @dev Revert with an error when attempting to set the provenance
               *      hash after the mint has started.
               */
              error ProvenanceHashCannotBeSetAfterMintStarted();
              /**
               * @dev Revert if the royalty basis points is greater than 10_000.
               */
              error InvalidRoyaltyBasisPoints(uint256 basisPoints);
              /**
               * @dev Revert if the royalty address is being set to the zero address.
               */
              error RoyaltyAddressCannotBeZeroAddress();
              /**
               * @dev Emit an event for token metadata reveals/updates,
               *      according to EIP-4906.
               *
               * @param _fromTokenId The start token id.
               * @param _toTokenId   The end token id.
               */
              event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
              /**
               * @dev Emit an event when the URI for the collection-level metadata
               *      is updated.
               */
              event ContractURIUpdated(string newContractURI);
              /**
               * @dev Emit an event when the max token supply is updated.
               */
              event MaxSupplyUpdated(uint256 newMaxSupply);
              /**
               * @dev Emit an event with the previous and new provenance hash after
               *      being updated.
               */
              event ProvenanceHashUpdated(bytes32 previousHash, bytes32 newHash);
              /**
               * @dev Emit an event when the royalties info is updated.
               */
              event RoyaltyInfoUpdated(address receiver, uint256 bps);
              /**
               * @notice A struct defining royalty info for the contract.
               */
              struct RoyaltyInfo {
                  address royaltyAddress;
                  uint96 royaltyBps;
              }
              /**
               * @notice Sets the base URI for the token metadata and emits an event.
               *
               * @param tokenURI The new base URI to set.
               */
              function setBaseURI(string calldata tokenURI) external;
              /**
               * @notice Sets the contract URI for contract metadata.
               *
               * @param newContractURI The new contract URI.
               */
              function setContractURI(string calldata newContractURI) external;
              /**
               * @notice Sets the max supply and emits an event.
               *
               * @param newMaxSupply The new max supply to set.
               */
              function setMaxSupply(uint256 newMaxSupply) external;
              /**
               * @notice Sets the provenance hash and emits an event.
               *
               *         The provenance hash is used for random reveals, which
               *         is a hash of the ordered metadata to show it has not been
               *         modified after mint started.
               *
               *         This function will revert after the first item has been minted.
               *
               * @param newProvenanceHash The new provenance hash to set.
               */
              function setProvenanceHash(bytes32 newProvenanceHash) external;
              /**
               * @notice Sets the address and basis points for royalties.
               *
               * @param newInfo The struct to configure royalties.
               */
              function setRoyaltyInfo(RoyaltyInfo calldata newInfo) external;
              /**
               * @notice Returns the base URI for token metadata.
               */
              function baseURI() external view returns (string memory);
              /**
               * @notice Returns the contract URI.
               */
              function contractURI() external view returns (string memory);
              /**
               * @notice Returns the max token supply.
               */
              function maxSupply() external view returns (uint256);
              /**
               * @notice Returns the provenance hash.
               *         The provenance hash is used for random reveals, which
               *         is a hash of the ordered metadata to show it is unmodified
               *         after mint has started.
               */
              function provenanceHash() external view returns (bytes32);
              /**
               * @notice Returns the address that receives royalties.
               */
              function royaltyAddress() external view returns (address);
              /**
               * @notice Returns the royalty basis points out of 10_000.
               */
              function royaltyBasisPoints() external view returns (uint256);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
            AllowListData,
            PublicDrop,
            SignedMintValidationParams,
            TokenGatedDropStage
          } from "./SeaDropStructs.sol";
          interface ERC721SeaDropStructsErrorsAndEvents {
            /**
             * @notice Revert with an error if mint exceeds the max supply.
             */
            error MintQuantityExceedsMaxSupply(uint256 total, uint256 maxSupply);
            /**
             * @notice Revert with an error if the number of token gated 
             *         allowedNftTokens doesn't match the length of supplied
             *         drop stages.
             */
            error TokenGatedMismatch();
            /**
             *  @notice Revert with an error if the number of signers doesn't match
             *          the length of supplied signedMintValidationParams
             */
            error SignersMismatch();
            /**
             * @notice An event to signify that a SeaDrop token contract was deployed.
             */
            event SeaDropTokenDeployed();
            /**
             * @notice A struct to configure multiple contract options at a time.
             */
            struct MultiConfigureStruct {
              uint256 maxSupply;
              string baseURI;
              string contractURI;
              address seaDropImpl;
              PublicDrop publicDrop;
              string dropURI;
              AllowListData allowListData;
              address creatorPayoutAddress;
              bytes32 provenanceHash;
              address[] allowedFeeRecipients;
              address[] disallowedFeeRecipients;
              address[] allowedPayers;
              address[] disallowedPayers;
              // Token-gated
              address[] tokenGatedAllowedNftTokens;
              TokenGatedDropStage[] tokenGatedDropStages;
              address[] disallowedTokenGatedAllowedNftTokens;
              // Server-signed
              address[] signers;
              SignedMintValidationParams[] signedMintValidationParams;
              address[] disallowedSigners;
            }
          }// SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { PublicDrop, TokenGatedDropStage, SignedMintValidationParams } from "./SeaDropStructs.sol";
          interface SeaDropErrorsAndEvents {
              /**
               * @dev Revert with an error if the drop stage is not active.
               */
              error NotActive(
                  uint256 currentTimestamp,
                  uint256 startTimestamp,
                  uint256 endTimestamp
              );
              /**
               * @dev Revert with an error if the mint quantity is zero.
               */
              error MintQuantityCannotBeZero();
              /**
               * @dev Revert with an error if the mint quantity exceeds the max allowed
               *      to be minted per wallet.
               */
              error MintQuantityExceedsMaxMintedPerWallet(uint256 total, uint256 allowed);
              /**
               * @dev Revert with an error if the mint quantity exceeds the max token
               *      supply.
               */
              error MintQuantityExceedsMaxSupply(uint256 total, uint256 maxSupply);
              /**
               * @dev Revert with an error if the mint quantity exceeds the max token
               *      supply for the stage.
               *      Note: The `maxTokenSupplyForStage` for public mint is
               *      always `type(uint).max`.
               */
              error MintQuantityExceedsMaxTokenSupplyForStage(
                  uint256 total, 
                  uint256 maxTokenSupplyForStage
              );
              
              /**
               * @dev Revert if the fee recipient is the zero address.
               */
              error FeeRecipientCannotBeZeroAddress();
              /**
               * @dev Revert if the fee recipient is not already included.
               */
              error FeeRecipientNotPresent();
              /**
               * @dev Revert if the fee basis points is greater than 10_000.
               */
              error InvalidFeeBps(uint256 feeBps);
              /**
               * @dev Revert if the fee recipient is already included.
               */
              error DuplicateFeeRecipient();
              /**
               * @dev Revert if the fee recipient is restricted and not allowed.
               */
              error FeeRecipientNotAllowed();
              /**
               * @dev Revert if the creator payout address is the zero address.
               */
              error CreatorPayoutAddressCannotBeZeroAddress();
              /**
               * @dev Revert with an error if the received payment is incorrect.
               */
              error IncorrectPayment(uint256 got, uint256 want);
              /**
               * @dev Revert with an error if the allow list proof is invalid.
               */
              error InvalidProof();
              /**
               * @dev Revert if a supplied signer address is the zero address.
               */
              error SignerCannotBeZeroAddress();
              /**
               * @dev Revert with an error if signer's signature is invalid.
               */
              error InvalidSignature(address recoveredSigner);
              /**
               * @dev Revert with an error if a signer is not included in
               *      the enumeration when removing.
               */
              error SignerNotPresent();
              /**
               * @dev Revert with an error if a payer is not included in
               *      the enumeration when removing.
               */
              error PayerNotPresent();
              /**
               * @dev Revert with an error if a payer is already included in mapping
               *      when adding.
               *      Note: only applies when adding a single payer, as duplicates in
               *      enumeration can be removed with updatePayer.
               */
              error DuplicatePayer();
              /**
               * @dev Revert with an error if the payer is not allowed. The minter must
               *      pay for their own mint.
               */
              error PayerNotAllowed();
              /**
               * @dev Revert if a supplied payer address is the zero address.
               */
              error PayerCannotBeZeroAddress();
              /**
               * @dev Revert with an error if the sender does not
               *      match the INonFungibleSeaDropToken interface.
               */
              error OnlyINonFungibleSeaDropToken(address sender);
              /**
               * @dev Revert with an error if the sender of a token gated supplied
               *      drop stage redeem is not the owner of the token.
               */
              error TokenGatedNotTokenOwner(
                  address nftContract,
                  address allowedNftToken,
                  uint256 allowedNftTokenId
              );
              /**
               * @dev Revert with an error if the token id has already been used to
               *      redeem a token gated drop stage.
               */
              error TokenGatedTokenIdAlreadyRedeemed(
                  address nftContract,
                  address allowedNftToken,
                  uint256 allowedNftTokenId
              );
              /**
               * @dev Revert with an error if an empty TokenGatedDropStage is provided
               *      for an already-empty TokenGatedDropStage.
               */
               error TokenGatedDropStageNotPresent();
              /**
               * @dev Revert with an error if an allowedNftToken is set to
               *      the zero address.
               */
               error TokenGatedDropAllowedNftTokenCannotBeZeroAddress();
              /**
               * @dev Revert with an error if an allowedNftToken is set to
               *      the drop token itself.
               */
               error TokenGatedDropAllowedNftTokenCannotBeDropToken();
              /**
               * @dev Revert with an error if supplied signed mint price is less than
               *      the minimum specified.
               */
              error InvalidSignedMintPrice(uint256 got, uint256 minimum);
              /**
               * @dev Revert with an error if supplied signed maxTotalMintableByWallet
               *      is greater than the maximum specified.
               */
              error InvalidSignedMaxTotalMintableByWallet(uint256 got, uint256 maximum);
              /**
               * @dev Revert with an error if supplied signed start time is less than
               *      the minimum specified.
               */
              error InvalidSignedStartTime(uint256 got, uint256 minimum);
              
              /**
               * @dev Revert with an error if supplied signed end time is greater than
               *      the maximum specified.
               */
              error InvalidSignedEndTime(uint256 got, uint256 maximum);
              /**
               * @dev Revert with an error if supplied signed maxTokenSupplyForStage
               *      is greater than the maximum specified.
               */
               error InvalidSignedMaxTokenSupplyForStage(uint256 got, uint256 maximum);
              
               /**
               * @dev Revert with an error if supplied signed feeBps is greater than
               *      the maximum specified, or less than the minimum.
               */
              error InvalidSignedFeeBps(uint256 got, uint256 minimumOrMaximum);
              /**
               * @dev Revert with an error if signed mint did not specify to restrict
               *      fee recipients.
               */
              error SignedMintsMustRestrictFeeRecipients();
              /**
               * @dev Revert with an error if a signature for a signed mint has already
               *      been used.
               */
              error SignatureAlreadyUsed();
              /**
               * @dev An event with details of a SeaDrop mint, for analytical purposes.
               * 
               * @param nftContract    The nft contract.
               * @param minter         The mint recipient.
               * @param feeRecipient   The fee recipient.
               * @param payer          The address who payed for the tx.
               * @param quantityMinted The number of tokens minted.
               * @param unitMintPrice  The amount paid for each token.
               * @param feeBps         The fee out of 10_000 basis points collected.
               * @param dropStageIndex The drop stage index. Items minted
               *                       through mintPublic() have
               *                       dropStageIndex of 0.
               */
              event SeaDropMint(
                  address indexed nftContract,
                  address indexed minter,
                  address indexed feeRecipient,
                  address payer,
                  uint256 quantityMinted,
                  uint256 unitMintPrice,
                  uint256 feeBps,
                  uint256 dropStageIndex
              );
              /**
               * @dev An event with updated public drop data for an nft contract.
               */
              event PublicDropUpdated(
                  address indexed nftContract,
                  PublicDrop publicDrop
              );
              /**
               * @dev An event with updated token gated drop stage data
               *      for an nft contract.
               */
              event TokenGatedDropStageUpdated(
                  address indexed nftContract,
                  address indexed allowedNftToken,
                  TokenGatedDropStage dropStage
              );
              /**
               * @dev An event with updated allow list data for an nft contract.
               * 
               * @param nftContract        The nft contract.
               * @param previousMerkleRoot The previous allow list merkle root.
               * @param newMerkleRoot      The new allow list merkle root.
               * @param publicKeyURI       If the allow list is encrypted, the public key
               *                           URIs that can decrypt the list.
               *                           Empty if unencrypted.
               * @param allowListURI       The URI for the allow list.
               */
              event AllowListUpdated(
                  address indexed nftContract,
                  bytes32 indexed previousMerkleRoot,
                  bytes32 indexed newMerkleRoot,
                  string[] publicKeyURI,
                  string allowListURI
              );
              /**
               * @dev An event with updated drop URI for an nft contract.
               */
              event DropURIUpdated(address indexed nftContract, string newDropURI);
              /**
               * @dev An event with the updated creator payout address for an nft
               *      contract.
               */
              event CreatorPayoutAddressUpdated(
                  address indexed nftContract,
                  address indexed newPayoutAddress
              );
              /**
               * @dev An event with the updated allowed fee recipient for an nft
               *      contract.
               */
              event AllowedFeeRecipientUpdated(
                  address indexed nftContract,
                  address indexed feeRecipient,
                  bool indexed allowed
              );
              /**
               * @dev An event with the updated validation parameters for server-side
               *      signers.
               */
              event SignedMintValidationParamsUpdated(
                  address indexed nftContract,
                  address indexed signer,
                  SignedMintValidationParams signedMintValidationParams
              );   
              /**
               * @dev An event with the updated payer for an nft contract.
               */
              event PayerUpdated(
                  address indexed nftContract,
                  address indexed payer,
                  bool indexed allowed
              );
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          /**
           * @notice A struct defining public drop data.
           *         Designed to fit efficiently in one storage slot.
           * 
           * @param mintPrice                The mint price per token. (Up to 1.2m
           *                                 of native token, e.g. ETH, MATIC)
           * @param startTime                The start time, ensure this is not zero.
           * @param endTIme                  The end time, ensure this is not zero.
           * @param maxTotalMintableByWallet Maximum total number of mints a user is
           *                                 allowed. (The limit for this field is
           *                                 2^16 - 1)
           * @param feeBps                   Fee out of 10_000 basis points to be
           *                                 collected.
           * @param restrictFeeRecipients    If false, allow any fee recipient;
           *                                 if true, check fee recipient is allowed.
           */
          struct PublicDrop {
              uint80 mintPrice; // 80/256 bits
              uint48 startTime; // 128/256 bits
              uint48 endTime; // 176/256 bits
              uint16 maxTotalMintableByWallet; // 224/256 bits
              uint16 feeBps; // 240/256 bits
              bool restrictFeeRecipients; // 248/256 bits
          }
          /**
           * @notice A struct defining token gated drop stage data.
           *         Designed to fit efficiently in one storage slot.
           * 
           * @param mintPrice                The mint price per token. (Up to 1.2m 
           *                                 of native token, e.g.: ETH, MATIC)
           * @param maxTotalMintableByWallet Maximum total number of mints a user is
           *                                 allowed. (The limit for this field is
           *                                 2^16 - 1)
           * @param startTime                The start time, ensure this is not zero.
           * @param endTime                  The end time, ensure this is not zero.
           * @param dropStageIndex           The drop stage index to emit with the event
           *                                 for analytical purposes. This should be 
           *                                 non-zero since the public mint emits
           *                                 with index zero.
           * @param maxTokenSupplyForStage   The limit of token supply this stage can
           *                                 mint within. (The limit for this field is
           *                                 2^16 - 1)
           * @param feeBps                   Fee out of 10_000 basis points to be
           *                                 collected.
           * @param restrictFeeRecipients    If false, allow any fee recipient;
           *                                 if true, check fee recipient is allowed.
           */
          struct TokenGatedDropStage {
              uint80 mintPrice; // 80/256 bits
              uint16 maxTotalMintableByWallet; // 96/256 bits
              uint48 startTime; // 144/256 bits
              uint48 endTime; // 192/256 bits
              uint8 dropStageIndex; // non-zero. 200/256 bits
              uint32 maxTokenSupplyForStage; // 232/256 bits
              uint16 feeBps; // 248/256 bits
              bool restrictFeeRecipients; // 256/256 bits
          }
          /**
           * @notice A struct defining mint params for an allow list.
           *         An allow list leaf will be composed of `msg.sender` and
           *         the following params.
           * 
           *         Note: Since feeBps is encoded in the leaf, backend should ensure
           *         that feeBps is acceptable before generating a proof.
           * 
           * @param mintPrice                The mint price per token.
           * @param maxTotalMintableByWallet Maximum total number of mints a user is
           *                                 allowed.
           * @param startTime                The start time, ensure this is not zero.
           * @param endTime                  The end time, ensure this is not zero.
           * @param dropStageIndex           The drop stage index to emit with the event
           *                                 for analytical purposes. This should be
           *                                 non-zero since the public mint emits with
           *                                 index zero.
           * @param maxTokenSupplyForStage   The limit of token supply this stage can
           *                                 mint within.
           * @param feeBps                   Fee out of 10_000 basis points to be
           *                                 collected.
           * @param restrictFeeRecipients    If false, allow any fee recipient;
           *                                 if true, check fee recipient is allowed.
           */
          struct MintParams {
              uint256 mintPrice; 
              uint256 maxTotalMintableByWallet;
              uint256 startTime;
              uint256 endTime;
              uint256 dropStageIndex; // non-zero
              uint256 maxTokenSupplyForStage;
              uint256 feeBps;
              bool restrictFeeRecipients;
          }
          /**
           * @notice A struct defining token gated mint params.
           * 
           * @param allowedNftToken    The allowed nft token contract address.
           * @param allowedNftTokenIds The token ids to redeem.
           */
          struct TokenGatedMintParams {
              address allowedNftToken;
              uint256[] allowedNftTokenIds;
          }
          /**
           * @notice A struct defining allow list data (for minting an allow list).
           * 
           * @param merkleRoot    The merkle root for the allow list.
           * @param publicKeyURIs If the allowListURI is encrypted, a list of URIs
           *                      pointing to the public keys. Empty if unencrypted.
           * @param allowListURI  The URI for the allow list.
           */
          struct AllowListData {
              bytes32 merkleRoot;
              string[] publicKeyURIs;
              string allowListURI;
          }
          /**
           * @notice A struct defining minimum and maximum parameters to validate for 
           *         signed mints, to minimize negative effects of a compromised signer.
           *
           * @param minMintPrice                The minimum mint price allowed.
           * @param maxMaxTotalMintableByWallet The maximum total number of mints allowed
           *                                    by a wallet.
           * @param minStartTime                The minimum start time allowed.
           * @param maxEndTime                  The maximum end time allowed.
           * @param maxMaxTokenSupplyForStage   The maximum token supply allowed.
           * @param minFeeBps                   The minimum fee allowed.
           * @param maxFeeBps                   The maximum fee allowed.
           */
          struct SignedMintValidationParams {
              uint80 minMintPrice; // 80/256 bits
              uint24 maxMaxTotalMintableByWallet; // 104/256 bits
              uint40 minStartTime; // 144/256 bits
              uint40 maxEndTime; // 184/256 bits
              uint40 maxMaxTokenSupplyForStage; // 224/256 bits
              uint16 minFeeBps; // 240/256 bits
              uint16 maxFeeBps; // 256/256 bits
          }

          File 3 of 7: 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 4 of 7: PolicyManager
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
          import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
          import {IPolicyManager} from "./interfaces/IPolicyManager.sol";
          /**
           * @title PolicyManager
           * @dev Manages the policy whitelist for the Blur exchange
           */
          contract PolicyManager is IPolicyManager, Ownable {
              using EnumerableSet for EnumerableSet.AddressSet;
              EnumerableSet.AddressSet private _whitelistedPolicies;
              event PolicyRemoved(address indexed policy);
              event PolicyWhitelisted(address indexed policy);
              /**
               * @notice Add matching policy
               * @param policy address of policy to add
               */
              function addPolicy(address policy) external override onlyOwner {
                  require(!_whitelistedPolicies.contains(policy), "Already whitelisted");
                  _whitelistedPolicies.add(policy);
                  emit PolicyWhitelisted(policy);
              }
              /**
               * @notice Remove matching policy
               * @param policy address of policy to remove
               */
              function removePolicy(address policy) external override onlyOwner {
                  require(_whitelistedPolicies.contains(policy), "Not whitelisted");
                  _whitelistedPolicies.remove(policy);
                  emit PolicyRemoved(policy);
              }
              /**
               * @notice Returns if a policy has been added
               * @param policy address of the policy to check
               */
              function isPolicyWhitelisted(address policy) external view override returns (bool) {
                  return _whitelistedPolicies.contains(policy);
              }
              /**
               * @notice View number of whitelisted policies
               */
              function viewCountWhitelistedPolicies() external view override returns (uint256) {
                  return _whitelistedPolicies.length();
              }
              /**
               * @notice See whitelisted policies
               * @param cursor cursor
               * @param size size
               */
              function viewWhitelistedPolicies(uint256 cursor, uint256 size)
                  external
                  view
                  override
                  returns (address[] memory, uint256)
              {
                  uint256 length = size;
                  if (length > _whitelistedPolicies.length() - cursor) {
                      length = _whitelistedPolicies.length() - cursor;
                  }
                  address[] memory whitelistedPolicies = new address[](length);
                  for (uint256 i = 0; i < length; i++) {
                      whitelistedPolicies[i] = _whitelistedPolicies.at(cursor + i);
                  }
                  return (whitelistedPolicies, cursor + length);
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (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 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.
               */
              modifier onlyOwner() {
                  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/structs/EnumerableSet.sol)
          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.
           */
          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) {
                  return _values(set._inner);
              }
              // 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;
                  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 on 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;
                  assembly {
                      result := store
                  }
                  return result;
              }
          }
          // 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
          // 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 5 of 7: 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 6 of 7: ExecutionDelegate
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
          import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
          import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
          import "@openzeppelin/contracts/access/Ownable.sol";
          import "@openzeppelin/contracts/utils/Address.sol";
          import {IExecutionDelegate} from "./interfaces/IExecutionDelegate.sol";
          /**
           * @title ExecutionDelegate
           * @dev Proxy contract to manage user token approvals
           */
          contract ExecutionDelegate is IExecutionDelegate, Ownable {
              using Address for address;
              mapping(address => bool) public contracts;
              mapping(address => bool) public revokedApproval;
              modifier approvedContract() {
                  require(contracts[msg.sender], "Contract is not approved to make transfers");
                  _;
              }
              event ApproveContract(address indexed _contract);
              event DenyContract(address indexed _contract);
              event RevokeApproval(address indexed user);
              event GrantApproval(address indexed user);
              /**
               * @dev Approve contract to call transfer functions
               * @param _contract address of contract to approve
               */
              function approveContract(address _contract) onlyOwner external {
                  contracts[_contract] = true;
                  emit ApproveContract(_contract);
              }
              /**
               * @dev Revoke approval of contract to call transfer functions
               * @param _contract address of contract to revoke approval
               */
              function denyContract(address _contract) onlyOwner external {
                  contracts[_contract] = false;
                  emit DenyContract(_contract);
              }
              /**
               * @dev Block contract from making transfers on-behalf of a specific user
               */
              function revokeApproval() external {
                  revokedApproval[msg.sender] = true;
                  emit RevokeApproval(msg.sender);
              }
              /**
               * @dev Allow contract to make transfers on-behalf of a specific user
               */
              function grantApproval() external {
                  revokedApproval[msg.sender] = false;
                  emit GrantApproval(msg.sender);
              }
              /**
               * @dev Transfer ERC721 token using `transferFrom`
               * @param collection address of the collection
               * @param from address of the sender
               * @param to address of the recipient
               * @param tokenId tokenId
               */
              function transferERC721Unsafe(address collection, address from, address to, uint256 tokenId)
                  approvedContract
                  external
              {
                  require(revokedApproval[from] == false, "User has revoked approval");
                  IERC721(collection).transferFrom(from, to, tokenId);
              }
              /**
               * @dev Transfer ERC721 token using `safeTransferFrom`
               * @param collection address of the collection
               * @param from address of the sender
               * @param to address of the recipient
               * @param tokenId tokenId
               */
              function transferERC721(address collection, address from, address to, uint256 tokenId)
                  approvedContract
                  external
              {
                  require(revokedApproval[from] == false, "User has revoked approval");
                  IERC721(collection).safeTransferFrom(from, to, tokenId);
              }
              /**
               * @dev Transfer ERC1155 token using `safeTransferFrom`
               * @param collection address of the collection
               * @param from address of the sender
               * @param to address of the recipient
               * @param tokenId tokenId
               * @param amount amount
               */
              function transferERC1155(address collection, address from, address to, uint256 tokenId, uint256 amount)
                  approvedContract
                  external
              {
                  require(revokedApproval[from] == false, "User has revoked approval");
                  IERC1155(collection).safeTransferFrom(from, to, tokenId, amount, "");
              }
              /**
               * @dev Transfer ERC20 token
               * @param token address of the token
               * @param from address of the sender
               * @param to address of the recipient
               * @param amount amount
               */
              function transferERC20(address token, address from, address to, uint256 amount)
                  approvedContract
                  external
              {
                  require(revokedApproval[from] == false, "User has revoked approval");
                  bytes memory data = abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount);
                  bytes memory returndata = token.functionCall(data);
                  if (returndata.length > 0) {
                    require(abi.decode(returndata, (bool)), "ERC20 transfer failed");
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           */
          interface IERC20 {
              /**
               * @dev Returns the amount of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
              /**
               * @dev Returns the amount of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
              /**
               * @dev Moves `amount` tokens from the caller's account to `recipient`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address recipient, uint256 amount) external returns (bool);
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
              /**
               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 amount) external returns (bool);
              /**
               * @dev Moves `amount` tokens from `sender` to `recipient` using the
               * allowance mechanism. `amount` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
              ) external returns (bool);
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
          pragma solidity ^0.8.0;
          import "../../utils/introspection/IERC165.sol";
          /**
           * @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;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)
          pragma solidity ^0.8.0;
          import "../../utils/introspection/IERC165.sol";
          /**
           * @dev Required interface of an ERC1155 compliant contract, as defined in the
           * https://eips.ethereum.org/EIPS/eip-1155[EIP].
           *
           * _Available since v3.1._
           */
          interface IERC1155 is IERC165 {
              /**
               * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
               */
              event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
              /**
               * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
               * transfers.
               */
              event TransferBatch(
                  address indexed operator,
                  address indexed from,
                  address indexed to,
                  uint256[] ids,
                  uint256[] values
              );
              /**
               * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
               * `approved`.
               */
              event ApprovalForAll(address indexed account, address indexed operator, bool approved);
              /**
               * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
               *
               * If an {URI} event was emitted for `id`, the standard
               * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
               * returned by {IERC1155MetadataURI-uri}.
               */
              event URI(string value, uint256 indexed id);
              /**
               * @dev Returns the amount of tokens of token type `id` owned by `account`.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function balanceOf(address account, uint256 id) external view returns (uint256);
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
               *
               * Requirements:
               *
               * - `accounts` and `ids` must have the same length.
               */
              function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
                  external
                  view
                  returns (uint256[] memory);
              /**
               * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
               *
               * Emits an {ApprovalForAll} event.
               *
               * Requirements:
               *
               * - `operator` cannot be the caller.
               */
              function setApprovalForAll(address operator, bool approved) external;
              /**
               * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
               *
               * See {setApprovalForAll}.
               */
              function isApprovedForAll(address account, address operator) external view returns (bool);
              /**
               * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
               * - `from` must have a balance of tokens of type `id` of at least `amount`.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes calldata data
              ) external;
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] calldata ids,
                  uint256[] calldata amounts,
                  bytes calldata data
              ) external;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (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 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.
               */
              modifier onlyOwner() {
                  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/Address.sol)
          pragma solidity ^0.8.0;
          /**
           * @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
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize, which returns 0 for contracts in
                  // construction, since the code is only stored at the end of the
                  // constructor execution.
                  uint256 size;
                  assembly {
                      size := extcodesize(account)
                  }
                  return size > 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);
                      }
                  }
              }
          }
          // 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
          // 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);
          }
          // 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;
              }
          }
          

          File 7 of 7: 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);
          }