ETH Price: $3,600.55 (-0.79%)

Transaction Decoder

Block:
22408635 at May-04-2025 06:31:47 AM +UTC
Transaction Fee:
0.000097193064507404 ETH $0.35
Gas Used:
70,948 Gas / 1.369919723 Gwei

Emitted Events:

210 TransparentUpgradeableProxy.0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31( 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31, 0x0000000000000000000000005b70facd084315d7466ab04ddf7d063f6569f71b, 0x0000000000000000000000001e0049783f008a0085193e00003d00cd54003c71, 0000000000000000000000000000000000000000000000000000000000000001 )

Account State Difference:

  Address   Before After State Difference Code
0x5B70fAcd...f6569f71b
0.0050944 Eth
Nonce: 0
0.004997206935492596 Eth
Nonce: 1
0.000097193064507404
0x5CC5B05a...e50F90c38
(beaverbuild)
17.399502747341709362 Eth17.399573695341709362 Eth0.000070948

Execution Trace

TransparentUpgradeableProxy.a22cb465( )
  • LandV3.setApprovalForAll( operator=0x1E0049783F008A0085193E00003D00cd54003c71, approved=True )
    • OperatorFilterRegistry.isOperatorAllowed( registrant=0x5CC5B05a8A13E3fBDB0BB9FcCd98D38e50F90c38, operator=0x1E0049783F008A0085193E00003D00cd54003c71 ) => ( True )
      setApprovalForAll[LandV3 (ln:1543)]
      File 1 of 3: TransparentUpgradeableProxy
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.0;
      /**
       * @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 {
              // solhint-disable-next-line no-inline-assembly
              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 virtual view 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 {
              _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 () payable external {
              _fallback();
          }
          /**
           * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
           * is empty.
           */
          receive () payable external {
              _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 {
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.0;
      import "./UpgradeableProxy.sol";
      /**
       * @dev This contract implements a proxy that is upgradeable by an admin.
       * 
       * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
       * clashing], which can potentially be used in an attack, this contract uses the
       * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
       * things that go hand in hand:
       * 
       * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
       * that call matches one of the admin functions exposed by the proxy itself.
       * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
       * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
       * "admin cannot fallback to proxy target".
       * 
       * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
       * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
       * to sudden errors when trying to call a function from the proxy implementation.
       * 
       * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
       * you should think of the `ProxyAdmin` instance as the real administrative inerface of your proxy.
       */
      contract TransparentUpgradeableProxy is UpgradeableProxy {
          /**
           * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
           * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
           */
          constructor(address initialLogic, address initialAdmin, bytes memory _data) payable UpgradeableProxy(initialLogic, _data) {
              assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
              _setAdmin(initialAdmin);
          }
          /**
           * @dev Emitted when the admin account has changed.
           */
          event AdminChanged(address previousAdmin, address newAdmin);
          /**
           * @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 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
          /**
           * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
           */
          modifier ifAdmin() {
              if (msg.sender == _admin()) {
                  _;
              } else {
                  _fallback();
              }
          }
          /**
           * @dev Returns the current admin.
           * 
           * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
           * 
           * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
           * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
           * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
           */
          function admin() external ifAdmin returns (address) {
              return _admin();
          }
          /**
           * @dev Returns the current implementation.
           * 
           * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
           * 
           * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
           * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
           * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
           */
          function implementation() external ifAdmin returns (address) {
              return _implementation();
          }
          /**
           * @dev Changes the admin of the proxy.
           * 
           * Emits an {AdminChanged} event.
           * 
           * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
           */
          function changeAdmin(address newAdmin) external ifAdmin {
              require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
              emit AdminChanged(_admin(), newAdmin);
              _setAdmin(newAdmin);
          }
          /**
           * @dev Upgrade the implementation of the proxy.
           * 
           * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
           */
          function upgradeTo(address newImplementation) external ifAdmin {
              _upgradeTo(newImplementation);
          }
          /**
           * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
           * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
           * proxied contract.
           * 
           * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
           */
          function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
              _upgradeTo(newImplementation);
              // solhint-disable-next-line avoid-low-level-calls
              (bool success,) = newImplementation.delegatecall(data);
              require(success);
          }
          /**
           * @dev Returns the current admin.
           */
          function _admin() internal view returns (address adm) {
              bytes32 slot = _ADMIN_SLOT;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  adm := sload(slot)
              }
          }
          /**
           * @dev Stores a new address in the EIP1967 admin slot.
           */
          function _setAdmin(address newAdmin) private {
              bytes32 slot = _ADMIN_SLOT;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  sstore(slot, newAdmin)
              }
          }
          /**
           * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
           */
          function _beforeFallback() internal override virtual {
              require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
              super._beforeFallback();
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.0;
      import "./Proxy.sol";
      import "../utils/Address.sol";
      /**
       * @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.
       * 
       * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
       * {TransparentUpgradeableProxy}.
       */
      contract UpgradeableProxy is Proxy {
          /**
           * @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));
              _setImplementation(_logic);
              if(_data.length > 0) {
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success,) = _logic.delegatecall(_data);
                  require(success);
              }
          }
          /**
           * @dev Emitted when the implementation is upgraded.
           */
          event Upgraded(address indexed implementation);
          /**
           * @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 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          /**
           * @dev Returns the current implementation address.
           */
          function _implementation() internal override view returns (address impl) {
              bytes32 slot = _IMPLEMENTATION_SLOT;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  impl := sload(slot)
              }
          }
          /**
           * @dev Upgrades the proxy to a new implementation.
           * 
           * Emits an {Upgraded} event.
           */
          function _upgradeTo(address newImplementation) internal {
              _setImplementation(newImplementation);
              emit Upgraded(newImplementation);
          }
          /**
           * @dev Stores a new address in the EIP1967 implementation slot.
           */
          function _setImplementation(address newImplementation) private {
              require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");
              bytes32 slot = _IMPLEMENTATION_SLOT;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  sstore(slot, newImplementation)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.7.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) {
              // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
              // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
              // for accounts without code, i.e. `keccak256('')`
              bytes32 codehash;
              bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
              // solhint-disable-next-line no-inline-assembly
              assembly { codehash := extcodehash(account) }
              return (codehash != accountHash && codehash != 0x0);
          }
          /**
           * @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");
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (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");
              return _functionCallWithValue(target, data, value, errorMessage);
          }
          function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
              require(isContract(target), "Address: call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
              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
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      

      File 2 of 3: LandV3
      pragma solidity 0.5.9;
      contract AdminV2 {
          address internal _admin;
          event AdminChanged(address oldAdmin, address newAdmin);
          /// @notice gives the current administrator of this contract.
          /// @return the current administrator of this contract.
          function getAdmin() external view returns (address) {
              return _admin;
          }
          /// @notice change the administrator to be `newAdmin`.
          /// @param newAdmin address of the new administrator.
          function changeAdmin(address newAdmin) external {
              address admin = _admin;
              require(msg.sender == admin, "only admin can change admin");
              require(newAdmin != admin, "it can be only changed to a new admin");
              emit AdminChanged(admin, newAdmin);
              _admin = newAdmin;
          }
          modifier onlyAdmin() {
              require (msg.sender == _admin, "only admin allowed");
              _;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.5.9;
      import {AdminV2} from "./AdminV2.sol";
      import {AddressUtils} from "../../contracts_common/Libraries/AddressUtils.sol";
      /// @title MetaTransactionReceiverV2
      /// @author The Sandbox
      /// @notice Implements meta-transactions
      /// @dev This contract permits to give an address the capacity to perform meta-transactions on behalf of any address
      contract MetaTransactionReceiverV2 is AdminV2 {
          using AddressUtils for address;
          mapping(address => bool) internal _metaTransactionContracts;
          event MetaTransactionProcessor(address indexed metaTransactionProcessor, bool enabled);
          /// @notice Enable or disable the ability of `metaTransactionProcessor` to perform meta-tx (metaTransactionProcessor rights).
          /// @param metaTransactionProcessor address that will be given/removed metaTransactionProcessor rights.
          /// @param enabled set whether the metaTransactionProcessor is enabled or disabled.
          function setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) public onlyAdmin {
              require(
                  metaTransactionProcessor.isContract(),
                  "only contracts can be meta transaction processor"
              );
              _setMetaTransactionProcessor(metaTransactionProcessor, enabled);
          }
          /// @param metaTransactionProcessor address of the operator
          /// @param enabled is it enabled
          function _setMetaTransactionProcessor(address metaTransactionProcessor, bool enabled) internal {
              _metaTransactionContracts[metaTransactionProcessor] = enabled;
              emit MetaTransactionProcessor(metaTransactionProcessor, enabled);
          }
          /// @notice check whether address `who` is given meta-transaction execution rights.
          /// @param who The address to query.
          /// @return whether the address has meta-transaction execution rights.
          function isMetaTransactionProcessor(address who) external view returns(bool) {
              return _metaTransactionContracts[who];
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.5.9;
      import {AdminV2} from "./AdminV2.sol";
      /// @title SuperOperatorsV2
      /// @author The Sandbox
      /// @notice Implements a super operator role on the contract
      /// @dev The contract inheriting SuperOperatorsV2 is able to use a super operator role
      contract SuperOperatorsV2 is AdminV2 {
          mapping(address => bool) internal _superOperators;
          event SuperOperator(address indexed superOperator, bool enabled);
          /// @notice Enable or disable the ability of `superOperator` to transfer tokens of all (superOperator rights).
          /// @param superOperator address that will be given/removed superOperator right.
          /// @param enabled set whether the superOperator is enabled or disabled.
          function setSuperOperator(address superOperator, bool enabled) external onlyAdmin {
              require(
                  superOperator != address(0),
                  "address 0 is not allowed as super operator"
              );
              require(
                  enabled != _superOperators[superOperator],
                  "the status should be different than the current one"
              );
              _superOperators[superOperator] = enabled;
              emit SuperOperator(superOperator, enabled);
          }
          /// @notice check whether address `who` is given superOperator rights.
          /// @param who The address to query.
          /// @return whether the address has superOperator rights.
          function isSuperOperator(address who) public view returns (bool) {
              return _superOperators[who];
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.5.9;
      /**
       * @title ERC721 Non-Fungible Token Standard basic interface
       * @dev see https://eips.ethereum.org/EIPS/eip-721
       */
      interface ERC721Events {
          event Transfer(
              address indexed _from,
              address indexed _to,
              uint256 indexed _tokenId
          );
          event Approval(
              address indexed _owner,
              address indexed _approved,
              uint256 indexed _tokenId
          );
          event ApprovalForAll(
              address indexed _owner,
              address indexed _operator,
              bool _approved
          );
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.5.9;
      /**
       * @title ERC721MandatoryTokenReceiver
       * @author The Sandbox
       * @notice Interface for any contract that wants to support safeBatchTransfers
       * from ERC721 asset contracts.
       * @dev The ERC-165 identifier for this interface is 0x5e8bf644.
       */
      interface ERC721MandatoryTokenReceiver {
          /**
           * @notice Whenever tokens are transferred to this contract via {IERC721-safeBatchTransferFrom}
           * by `operator` from `from`, this function is called.
           * @param operator sender
           * @param from owner of the tokens
           * @param ids token ids
           * @param data extra data
           * @return 0x4b808c46 if the transfer is a success
           */
          function onERC721BatchReceived(
              address operator,
              address from,
              uint256[] calldata ids,
              bytes calldata data
          ) external returns (bytes4); // needs to return 0x4b808c46
          /**
           * @notice Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           * @param operator sender
           * @param from owner of the token
           * @param tokenId token id
           * @param data extra data
           * @return 0x150b7a02 if the transfer is a success
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4); // needs to return 0x150b7a02
      }
      // SPDX-License-Identifier: MIT
      // solhint-disable-next-line compiler-fixed
      pragma solidity 0.5.9;
      /**
       * @title ERC721TokenReceiver
       * @author The Sandbox
       * @notice Handle the receipt of an NFT
       */
      interface ERC721TokenReceiver {
          /**
           * @notice Handle the receipt of an NFT
           * @dev The ERC721 smart contract calls this function on the recipient
           * after a `transfer`. This function MAY throw to revert and reject the
           * transfer. Return of other than the magic value MUST result in the
           * transaction being reverted.
           * Note: the contract address is always the message sender.
           * @param operator The address which called `safeTransferFrom` function
           * @param from The address which previously owned the token
           * @param tokenId The NFT identifier which is being transferred
           * @param data Additional data with no specified format
           * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` unless throwing
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.5.9;
      /**
       * @title AddressUtils
       * @author The Sandbox
       * @notice Helper to manipulate addresses
       */
      library AddressUtils {
          /**
           * @dev Cast the address to be payable
           * @param _address target address
           * @return a payable address
           */
          function toPayable(address _address) internal pure returns (address payable) {
              return address(uint160(_address));
          }
          /**
           * @dev Check if the address is a contract
           * @param addr target address
           * @return is it a contract
           */
          function isContract(address addr) internal view returns (bool) {
              // for accounts without code, i.e. `keccak256('')`:
              bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
              bytes32 codehash;
              // solium-disable-next-line security/no-inline-assembly
              assembly {
                  codehash := extcodehash(addr)
              }
              return (codehash != 0x0 && codehash != accountHash);
          }
      }
      // SPDX-License-Identifier: MIT
      /* solhint-disable func-order, code-complexity */
      pragma solidity 0.5.9;
      import {AddressUtils} from "../../contracts_common/Libraries/AddressUtils.sol";
      import {ERC721TokenReceiver} from "../../contracts_common/Interfaces/ERC721TokenReceiver.sol";
      import {ERC721Events} from "../../contracts_common/Interfaces/ERC721Events.sol";
      import {SuperOperatorsV2} from "../../contracts_common/BaseWithStorage/SuperOperatorsV2.sol";
      import {MetaTransactionReceiverV2} from "../../contracts_common/BaseWithStorage/MetaTransactionReceiverV2.sol";
      import {ERC721MandatoryTokenReceiver} from "../../contracts_common/Interfaces/ERC721MandatoryTokenReceiver.sol";
      /**
       * @title ERC721BaseTokenV2
       * @author The Sandbox
       * @notice Basic functionalities of a NFT
       * @dev ERC721 implementation that supports meta-transactions and super operators
       */
      contract ERC721BaseTokenV2 is ERC721Events, SuperOperatorsV2, MetaTransactionReceiverV2 {
          using AddressUtils for address;
          bytes4 internal constant _ERC721_RECEIVED = 0x150b7a02;
          bytes4 internal constant _ERC721_BATCH_RECEIVED = 0x4b808c46;
          bytes4 internal constant ERC165ID = 0x01ffc9a7;
          bytes4 internal constant ERC721_MANDATORY_RECEIVER = 0x5e8bf644;
          /// @notice Number of NFT an address own
          mapping (address => uint256) public _numNFTPerAddress;
          /// @notice Token ids per address
          mapping (uint256 => uint256) public _owners;
          /// @notice Operators for each owner address for all tokens
          mapping (address => mapping(address => bool)) public _operatorsForAll;
          /// @notice Operator for each token id
          mapping (uint256 => address) public _operators;
          bool internal _initialized;
          modifier initializer() {
              require(!_initialized, "ERC721BaseToken: Contract already initialized");
              _;
          }
          /**
           * @notice Initializes the contract with the meta-transaction contract & admin
           * @param metaTransactionContract Authorized contract for meta-transactions
           * @param admin Admin of the contract
           */
          function initialize (
              address metaTransactionContract,
              address admin
          ) public initializer {
              _admin = admin;
              _setMetaTransactionProcessor(metaTransactionContract, true);
              _initialized = true;
              emit AdminChanged(address(0), _admin);
          }
          /**
           * @param from Sender address
           * @param to Recipient address
           * @param id Token id to transfer
           */
          function _transferFrom(
              address from,
              address to,
              uint256 id
          ) internal {
              _numNFTPerAddress[from]--;
              _numNFTPerAddress[to]++;
              _owners[id] = uint256(to);
              emit Transfer(from, to, id);
          }
          /**
           * @notice Return the number of Land owned by an address
           * @param owner The address to look for
           * @return The number of Land token owned by the address
           */
          function balanceOf(address owner) external view returns (uint256) {
              require(owner != address(0), "owner is zero address");
              return _numNFTPerAddress[owner];
          }
          /**
           * @param id token id
           * @return address of the owner
           */
          function _ownerOf(uint256 id) internal view returns (address) {
              return address(_owners[id]);
          }
          /**
           * @param id Token id
           * @return owner Address of the token's owner
           * @return operatorEnabled Is he an operator
           */
          function _ownerAndOperatorEnabledOf(uint256 id) internal view returns (address owner, bool operatorEnabled) {
              uint256 data = _owners[id];
              owner = address(data);
              operatorEnabled = (data / 2**255) == 1;
          }
          /**
           * @notice Return the owner of a Land
           * @param id The id of the Land
           * @return The address of the owner
           */
          function ownerOf(uint256 id) external view returns (address owner) {
              owner = _ownerOf(id);
              require(owner != address(0), "token does not exist");
          }
          /**
           * @param owner The address giving the approval
           * @param operator The address receiving the approval
           * @param id The id of the token
           */
          function _approveFor(
              address owner,
              address operator,
              uint256 id
          ) internal {
              if (operator == address(0)) {
                  _owners[id] = uint256(owner); // no need to resset the operator, it will be overriden next time
              } else {
                  _owners[id] = uint256(owner) + 2**255;
                  _operators[id] = operator;
              }
              emit Approval(owner, operator, id);
          }
          /**
           * @notice Approve an operator to spend tokens on the sender behalf
           * @param sender The address giving the approval
           * @param operator The address receiving the approval
           * @param id The id of the token
           */
          function approveFor(
              address sender,
              address operator,
              uint256 id
          ) public {
              address owner = _ownerOf(id);
              require(sender != address(0), "sender is zero address");
              require(
                  msg.sender == sender ||
                      _metaTransactionContracts[msg.sender] ||
                      _operatorsForAll[sender][msg.sender] ||
                      _superOperators[msg.sender],
                  "not authorized to approve"
              );
              require(owner == sender, "owner != sender");
              _approveFor(owner, operator, id);
          }
          /**
           * @notice Approve an operator to spend tokens on the sender behalf
           * @param operator The address receiving the approval
           * @param id The id of the token
           */
          function approve(address operator, uint256 id) public {
              address owner = _ownerOf(id);
              require(owner != address(0), "token does not exist");
              require(
                  owner == msg.sender || _operatorsForAll[owner][msg.sender] || _superOperators[msg.sender],
                  "not authorized to approve"
              );
              _approveFor(owner, operator, id);
          }
          /**
           * @notice Get the approved operator for a specific token
           * @param id The id of the token
           * @return The address of the operator
           */
          function getApproved(uint256 id) external view returns (address) {
              (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id);
              require(owner != address(0), "token does not exist");
              if (operatorEnabled) {
                  return _operators[id];
              } else {
                  return address(0);
              }
          }
          /**
           * @param from The sender of the token
           * @param to The recipient of the token
           * @param id The id of the token
           * @return is it a meta-tx
           */
          function _checkTransfer(
              address from,
              address to,
              uint256 id
          ) internal view returns (bool isMetaTx) {
              (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id);
              require(owner != address(0), "token does not exist");
              require(owner == from, "not owner in _checkTransfer");
              require(to != address(0), "can't send to zero address");
              if (msg.sender != from) {
                  if(_metaTransactionContracts[msg.sender]) {
                      return true;
                  }
                  require(
                      _operatorsForAll[from][msg.sender] ||
                          (operatorEnabled && _operators[id] == msg.sender) ||
                          _superOperators[msg.sender],
                      "not approved to transfer"
                  );
              }
          }
          /**
           * @dev Checks if the target contract supports the given interface & doesn't exceed 10000 gas
           * @param _contract The target contract
           * @param interfaceId The interface id
           * @return if the call is a success
           */
          function _checkInterfaceWith10000Gas(address _contract, bytes4 interfaceId)
              internal
              view
              returns (bool)
          {
              bool success;
              bool result;
              bytes memory call_data = abi.encodeWithSelector(ERC165ID, interfaceId);
              // solium-disable-next-line security/no-inline-assembly
              assembly {
                  let call_ptr := add(0x20, call_data)
                  let call_size := mload(call_data)
                  let output := mload(0x40) // Find empty storage location using "free memory pointer"
                  mstore(output, 0x0)
                  success := staticcall(10000, _contract, call_ptr, call_size, output, 0x20) // 32 bytes
                  result := mload(output)
              }
              // (10000 / 63) "not enough for supportsInterface(...)" // consume all gas, so caller can potentially know that there was not enough gas
              assert(gasleft() > 158);
              return success && result;
          }
          /**
           * @notice Transfer a token between 2 addresses
           * @param from The sender of the token
           * @param to The recipient of the token
           * @param id The id of the token
           */
          function transferFrom(
              address from,
              address to,
              uint256 id
          ) public {
              bool metaTx = _checkTransfer(from, to, id);
              _transferFrom(from, to, id);
              if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) {
                  require(
                      _checkOnERC721Received(metaTx ? from : msg.sender, from, to, id, ""),
                      "erc721 transfer rejected by to"
                  );
              }
          }
          /**
           * @notice Transfer a token between 2 addresses letting the receiver knows of the transfer
           * @param from The sender of the token
           * @param to The recipient of the token
           * @param id The id of the token
           * @param data Additional data
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              bytes memory data
          ) public {
              bool metaTx = _checkTransfer(from, to, id);
              _transferFrom(from, to, id);
              if (to.isContract()) {
                  require(
                      _checkOnERC721Received(metaTx ? from : msg.sender, from, to, id, data),
                      "ERC721: transfer rejected by to"
                  );
              }
          }
          /**
           * @notice Transfer a token between 2 addresses letting the receiver knows of the transfer
           * @param from The send of the token
           * @param to The recipient of the token
           * @param id The id of the token
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id
          ) external {
              safeTransferFrom(from, to, id, "");
          }
          /**
           * @notice Transfer many tokens between 2 addresses
           * @param from The sender of the token
           * @param to The recipient of the token
           * @param ids The ids of the tokens
           * @param data additional data
           */
          function batchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              bytes calldata data
          ) external {
              _batchTransferFrom(from, to, ids, data, false);
          }
          /**
           * @param from The sender of the token
           * @param to The recipient of the token
           * @param ids The ids of the tokens
           * @param data additional data
           * @param safe checks the target contract
           */
          function _batchTransferFrom(
              address from,
              address to,
              uint256[] memory ids,
              bytes memory data,
              bool safe
          ) internal {
              bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender];
              bool authorized =
                  msg.sender == from || metaTx || _operatorsForAll[from][msg.sender] || _superOperators[msg.sender];
              require(from != address(0), "from is zero address");
              require(to != address(0), "can't send to zero address");
              uint256 numTokens = ids.length;
              for (uint256 i = 0; i < numTokens; i++) {
                  uint256 id = ids[i];
                  (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id);
                  require(owner == from, "not owner in batchTransferFrom");
                  require(authorized || (operatorEnabled && _operators[id] == msg.sender), "not authorized");
                  _owners[id] = uint256(to);
                  emit Transfer(from, to, id);
              }
              if (from != to) {
                  _numNFTPerAddress[from] -= numTokens;
                  _numNFTPerAddress[to] += numTokens;
              }
              if (to.isContract()) {
                  if (_checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) {
                      require(
                          _checkOnERC721BatchReceived(metaTx ? from : msg.sender, from, to, ids, data),
                          "erc721 batch transfer rejected by to"
                      );
                  } else if (safe) {
                      for (uint256 i = 0; i < numTokens; i++) {
                          require(
                              _checkOnERC721Received(metaTx ? from : msg.sender, from, to, ids[i], ""),
                              "erc721 transfer rejected by to"
                          );
                      }
                  }
              }
          }
          /**
           * @notice Transfer many tokens between 2 addresses ensuring the receiving contract has a receiver method
           * @param from The sender of the token
           * @param to The recipient of the token
           * @param ids The ids of the tokens
           * @param data additional data
           */
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              bytes calldata data
          ) external {
              _batchTransferFrom(from, to, ids, data, true);
          }
          /**
           * @notice Check if the contract supports an interface
           * 0x01ffc9a7 is ERC-165
           * 0x80ac58cd is ERC-721
           * @param id The id of the interface
           * @return True if the interface is supported
           */
          function supportsInterface(bytes4 id) external pure returns (bool) {
              return id == 0x01ffc9a7 || id == 0x80ac58cd;
          }
          /**
           * @notice Set the approval for an operator to manage all the tokens of the sender
           * @param sender The address giving the approval
           * @param operator The address receiving the approval
           * @param approved The determination of the approval
           */
          function setApprovalForAllFor(
              address sender,
              address operator,
              bool approved
          ) public {
              require(sender != address(0), "Invalid sender address");
              require(
                  msg.sender == sender || _metaTransactionContracts[msg.sender] || _superOperators[msg.sender],
                  "not authorized to approve for all"
              );
              _setApprovalForAll(sender, operator, approved);
          }
          /**
           * @notice Set the approval for an operator to manage all the tokens of the sender
           * @param operator The address receiving the approval
           * @param approved The determination of the approval
           */
          function setApprovalForAll(address operator, bool approved) public {
              _setApprovalForAll(msg.sender, operator, approved);
          }
          /**
           * @param sender Sender address
           * @param operator The address receiving the approval
           * @param approved The determination of the approval
           */
          function _setApprovalForAll(
              address sender,
              address operator,
              bool approved
          ) internal {
              require(!_superOperators[operator], "super operator can't have their approvalForAll changed");
              _operatorsForAll[sender][operator] = approved;
              emit ApprovalForAll(sender, operator, approved);
          }
          /**
           * @notice Check if the sender approved the operator
           * @param owner The address of the owner
           * @param operator The address of the operator
           * @return The status of the approval
           */
          function isApprovedForAll(address owner, address operator)
              external
              view
              returns (bool)
          {
              return _operatorsForAll[owner][operator] || _superOperators[operator];
          }
          /**
           * @param from sender address
           * @param owner owner address of the token
           * @param id token id to burn
           */
          function _burn(
              address from,
              address owner,
              uint256 id
          ) internal {
              require(from == owner, "not owner");
              _owners[id] = 2**160; // cannot mint it again
              _numNFTPerAddress[from]--;
              emit Transfer(from, address(0), id);
          }
          /// @notice Burns token `id`.
          /// @param id token which will be burnt.
          function burn(uint256 id) external {
              _burn(msg.sender, _ownerOf(id), id);
          }
          /// @notice Burn token`id` from `from`.
          /// @param from address whose token is to be burnt.
          /// @param id token which will be burnt.
          function burnFrom(address from, uint256 id) external {
              require(from != address(0), "Invalid sender address");
              (address owner, bool operatorEnabled) = _ownerAndOperatorEnabledOf(id);
              require(
                  msg.sender == from ||
                      _metaTransactionContracts[msg.sender] ||
                      (operatorEnabled && _operators[id] == msg.sender) ||
                      _operatorsForAll[from][msg.sender] ||
                      _superOperators[msg.sender],
                  "not authorized to burn"
              );
              _burn(from, owner, id);
          }
          /**
           * @param operator Sender of the tx
           * @param from Owner of the token
           * @param to Recipient
           * @param tokenId Token id
           * @param _data extra data
           */
          function _checkOnERC721Received(
              address operator,
              address from,
              address to,
              uint256 tokenId,
              bytes memory _data
          ) internal returns (bool) {
              bytes4 retval = ERC721TokenReceiver(to).onERC721Received(operator, from, tokenId, _data);
              return (retval == _ERC721_RECEIVED);
          }
          /**
           * @dev Check if receiving contract accepts erc721 batch transfers.
           * @param operator Sender of the tx
           * @param from Owner of the token
           * @param to Recipient
           * @param ids Token ids
           * @param _data extra data
           * @return Whether the expected value of 0x4b808c46 is returned.
           */
          function _checkOnERC721BatchReceived(
              address operator,
              address from,
              address to,
              uint256[] memory ids,
              bytes memory _data
          ) internal returns (bool) {
              bytes4 retval = ERC721MandatoryTokenReceiver(to).onERC721BatchReceived(operator, from, ids, _data);
              return (retval == _ERC721_BATCH_RECEIVED);
          }
          // Empty storage space in contracts for future enhancements
          // ref: https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/issues/13)
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      /* solhint-disable func-order, code-complexity */
      pragma solidity 0.5.9;
      import {ERC721BaseTokenV2} from "./ERC721BaseTokenV2.sol";
      /**
       * @title LandBaseTokenV3
       * @author The Sandbox
       * @notice Implement LAND and quad functionalities on top of an ERC721 token
       * @dev This contract implements a quad tree structure to handle groups of ERC721 tokens at once
       */
      contract LandBaseTokenV3 is ERC721BaseTokenV2 {
          // Our grid is 408 x 408 lands
          uint256 internal constant GRID_SIZE = 408;
          uint256 internal constant LAYER = 0xFF00000000000000000000000000000000000000000000000000000000000000;
          uint256 internal constant LAYER_1x1 = 0x0000000000000000000000000000000000000000000000000000000000000000;
          uint256 internal constant LAYER_3x3 = 0x0100000000000000000000000000000000000000000000000000000000000000;
          uint256 internal constant LAYER_6x6 = 0x0200000000000000000000000000000000000000000000000000000000000000;
          uint256 internal constant LAYER_12x12 = 0x0300000000000000000000000000000000000000000000000000000000000000;
          uint256 internal constant LAYER_24x24 = 0x0400000000000000000000000000000000000000000000000000000000000000;
          mapping(address => bool) internal _minters;
          event Minter(address indexed superOperator, bool enabled);
          struct Land {
              uint256 x;
              uint256 y;
              uint256 size;
          }
          /**
           * @notice Mint a new quad (aligned to a quad tree with size 1, 3, 6, 12 or 24 only)
           * @param to The recipient of the new quad
           * @param size The size of the new quad
           * @param x The top left x coordinate of the new quad
           * @param y The top left y coordinate of the new quad
           * @param data extra data to pass to the transfer
           */
          function mintQuad(
              address to,
              uint256 size,
              uint256 x,
              uint256 y,
              bytes calldata data
          ) external {
              require(to != address(0), "to is zero address");
              require(size != 0, "size cannot be zero");
              require(isMinter(msg.sender), "Only a minter can mint");
              _isValidQuad(size, x, y);
              (uint256 layer, , ) = _getQuadLayer(size);
              uint256 quadId = _getQuadId(layer, x, y);
              _checkOwner(size, x, y, 24);
              for (uint256 i = 0; i < size * size; i++) {
                  uint256 _id = _idInPath(i, size, x, y);
                  require(_owners[_id] == 0, "Already minted");
                  emit Transfer(address(0), to, _id);
              }
              _owners[quadId] = uint256(to);
              _numNFTPerAddress[to] += size * size;
              _checkBatchReceiverAcceptQuad(msg.sender, address(0), to, size, x, y, data);
          }
          /**
           * @notice Checks if a parent quad has child quads already minted.
           *  Then mints the rest child quads and transfers the parent quad.
           *  Should only be called by the tunnel.
           * @param to The recipient of the new quad
           * @param size The size of the new quad
           * @param x The top left x coordinate of the new quad
           * @param y The top left y coordinate of the new quad
           * @param data extra data to pass to the transfer
           */
          function mintAndTransferQuad(
              address to,
              uint256 size,
              uint256 x,
              uint256 y,
              bytes calldata data
          ) external {
              require(to != address(0), "to is zero address");
              require(isMinter(msg.sender), "Only a minter can mint");
              if (exists(size, x, y) == true) {
                  _transferQuad(msg.sender, to, size, x, y);
                  _numNFTPerAddress[msg.sender] -= size * size;
                  _numNFTPerAddress[to] += size * size;
                  _checkBatchReceiverAcceptQuad(msg.sender, msg.sender, to, size, x, y, data);
              } else {
                  _mintAndTransferQuad(to, size, x, y, data);
              }
          }
          /// @notice transfer one quad (aligned to a quad tree with size 3, 6, 12 or 24 only)
          /// @param from current owner of the quad
          /// @param to destination
          /// @param size size of the quad
          /// @param x The top left x coordinate of the quad
          /// @param y The top left y coordinate of the quad
          /// @param data additional data
          function transferQuad(
              address from,
              address to,
              uint256 size,
              uint256 x,
              uint256 y,
              bytes calldata data
          ) external {
              require(from != address(0), "from is zero address");
              require(to != address(0), "can't send to zero address");
              bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender];
              if (msg.sender != from && !metaTx) {
                  require(
                      _operatorsForAll[from][msg.sender] || _superOperators[msg.sender],
                      "not authorized to transferQuad"
                  );
              }
              _transferQuad(from, to, size, x, y);
              _numNFTPerAddress[from] -= size * size;
              _numNFTPerAddress[to] += size * size;
              _checkBatchReceiverAcceptQuad(metaTx ? from : msg.sender, from, to, size, x, y, data);
          }
          /// @notice transfer multiple quad (aligned to a quad tree with size 3, 6, 12 or 24 only)
          /// @param from current owner of the quad
          /// @param to destination
          /// @param sizes list of sizes for each quad
          /// @param xs list of top left x coordinates for each quad
          /// @param ys list of top left y coordinates for each quad
          /// @param data additional data
          function batchTransferQuad(
              address from,
              address to,
              uint256[] calldata sizes,
              uint256[] calldata xs,
              uint256[] calldata ys,
              bytes calldata data
          ) external {
              require(from != address(0), "from is zero address");
              require(to != address(0), "can't send to zero address");
              require(sizes.length == xs.length, "LandBaseTokenV3: sizes's and x's length are different");
              require(xs.length == ys.length, "LandBaseTokenV3: x's and y's length are different");
              bool metaTx = msg.sender != from && _metaTransactionContracts[msg.sender];
              if (msg.sender != from && !metaTx) {
                  require(
                      _operatorsForAll[from][msg.sender] || _superOperators[msg.sender],
                      "not authorized to transferMultiQuads"
                  );
              }
              uint256 numTokensTransfered = 0;
              for (uint256 i = 0; i < sizes.length; i++) {
                  uint256 size = sizes[i];
                  _transferQuad(from, to, size, xs[i], ys[i]);
                  numTokensTransfered += size * size;
              }
              _numNFTPerAddress[from] -= numTokensTransfered;
              _numNFTPerAddress[to] += numTokensTransfered;
              if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) {
                  uint256[] memory ids = new uint256[](numTokensTransfered);
                  uint256 counter = 0;
                  for (uint256 j = 0; j < sizes.length; j++) {
                      uint256 size = sizes[j];
                      for (uint256 i = 0; i < size * size; i++) {
                          ids[counter] = _idInPath(i, size, xs[j], ys[j]);
                          counter++;
                      }
                  }
                  require(
                      _checkOnERC721BatchReceived(metaTx ? from : msg.sender, from, to, ids, data),
                      "erc721 batch transfer rejected by to"
                  );
              }
          }
          /// @notice Enable or disable the ability of `minter` to mint tokens
          /// @param minter address that will be given/removed minter right.
          /// @param enabled set whether the minter is enabled or disabled.
          function setMinter(address minter, bool enabled) external onlyAdmin {
              require(minter != address(0), "address 0 is not allowed as minter");
              require(enabled != _minters[minter], "the status should be different than the current one");
              _minters[minter] = enabled;
              emit Minter(minter, enabled);
          }
          /// @notice total width of the map
          /// @return width
          function width() external pure returns (uint256) {
              return GRID_SIZE;
          }
          /// @notice total height of the map
          /// @return height
          function height() external pure returns (uint256) {
              return GRID_SIZE;
          }
          /// @notice x coordinate of Land token
          /// @param id tokenId
          /// @return the x coordinates
          function getX(uint256 id) external pure returns (uint256) {
              return _getX(id);
          }
          /// @notice y coordinate of Land token
          /// @param id tokenId
          /// @return the y coordinates
          function getY(uint256 id) external pure returns (uint256) {
              return _getY(id);
          }
          /// @notice check whether address `who` is given minter rights.
          /// @param who The address to query.
          /// @return whether the address has minter rights.
          function isMinter(address who) public view returns (bool) {
              return _minters[who];
          }
          /// @notice checks if Land has been minted or not
          /// @param size size of the quad
          /// @param x x coordinate of the quad
          /// @param y y coordinate of the quad
          /// @return bool for if Land has been minted or not
          function exists(
              uint256 size,
              uint256 x,
              uint256 y
          ) public view returns (bool) {
              _isValidQuad(size, x, y);
              return _ownerOfQuad(size, x, y) != address(0);
          }
          function _isValidQuad(uint256 size, uint256 x, uint256 y) internal pure {
              require(size == 1 || size == 3 || size == 6 || size == 12 || size == 24, "Invalid size");
              require(x % size == 0, "Invalid x coordinate");
              require(y % size == 0, "Invalid y coordinate");
              require(x <= GRID_SIZE - size, "x out of bounds");
              require(y <= GRID_SIZE - size, "y out of bounds");
          }
          /**
           * @dev checks if the child quads in the parent quad (size, x, y) are owned by msg.sender.
           * It recursively checks child quad of every size(exculding Lands of 1x1 size) are minted or not.
           * Quad which are minted are pushed into quadMinted to also check if every Land of size 1x1 in the parent quad is minted or not.
           * While checking if the every child Quad and Land is minted it also checks and clear the owner for quads which are minted.
           * Finally it checks if the new owner if is a contract can handle ERC721 tokens or not and transfers the parent quad to new owner.
           * @param to The address to which the ownership of the quad will be transferred
           * @param size The size of the quad being minted and transfered
           * @param x The x-coordinate of the top-left corner of the quad being minted.
           * @param y The y-coordinate of the top-left corner of the quad being minted.
           * @param data extra data to pass to the transfer
          */
          function _mintAndTransferQuad(
              address to,
              uint256 size,
              uint256 x,
              uint256 y,
              bytes memory data
          ) internal {
              (uint256 layer, , ) = _getQuadLayer(size);
              uint256 quadId = _getQuadId(layer, x, y);
              // Length of array is equal to number of 3x3 child quad a 24x24 quad can have. Would be used to push the minted Quads.
              Land[] memory quadMinted = new Land[](64);
              // index of last minted quad pushed on quadMinted Array
              uint256 index;
              uint256 numLandMinted;
              // if size of the Quad in land struct to be transfered is greater than 3 we check recursivly if the child quads are minted or not.
              if (size > 3) {
                  (index, numLandMinted) = _checkAndClearOwner(
                      Land({x: x, y: y, size: size}),
                      quadMinted,
                      numLandMinted,
                      index,
                      size / 2
                  );
              }
              // Lopping around the Quad in land struct to generate ids of 1x1 land token and checking if they are owned by msg.sender
              {
                  for (uint256 i = 0; i < size * size; i++) {
                      uint256 _id = _idInPath(i, size, x, y);
                      // checking land with token id "_id" is in the quadMinted array.
                      bool isAlreadyMinted = _isQuadMinted(quadMinted, Land({x: _getX(_id), y: _getY(_id), size: 1}), index);
                      if (isAlreadyMinted) {
                          // if land is in the quadMinted array there just emitting transfer event.
                          emit Transfer(msg.sender, to, _id);
                      } else {
                          if (address(uint160(_owners[_id])) == msg.sender) {
                              if (_operators[_id] != address(0)) _operators[_id] = address(0);
                              numLandMinted += 1;
                              emit Transfer(msg.sender, to, _id);
                          } else {
                              // else is checked if owned by the msg.sender or not. If it is not owned by msg.sender it should not have an owner.
                              require(_owners[_id] == 0, "Already minted");
                              emit Transfer(address(0), to, _id);
                          }
                      }
                  }
              }
              // checking if the new owner "to" is a contract. If yes, checking if it could handle ERC721 tokens.
              _checkBatchReceiverAcceptQuadAndClearOwner(quadMinted, index, numLandMinted, to, size, x, y, data);
              _owners[quadId] = uint256(to);
              _numNFTPerAddress[to] += size * size;
              _numNFTPerAddress[msg.sender] -= numLandMinted;
          }
          /// @param operator sender of the tx
          /// @param from owner of the token
          /// @param to recipient
          /// @param size The size of the new quad
          /// @param x The top left x coordinate of the new quad
          /// @param y The top left y coordinate of the new quad
          /// @param data extra data
          function _checkBatchReceiverAcceptQuad(
              address operator,
              address from,
              address to,
              uint256 size,
              uint256 x,
              uint256 y,
              bytes memory data
          ) internal {
              if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) {
                  uint256[] memory ids = new uint256[](size * size);
                  for (uint256 i = 0; i < size * size; i++) {
                      ids[i] = _idInPath(i, size, x, y);
                  }
                  require(_checkOnERC721BatchReceived(operator, from, to, ids, data), "erc721 batch transfer rejected by to");
              }
          }
          /// @dev checks if the receiver of the quad(size, x, y) is a contact. If yes can it handle ERC721 tokens.
          ///      It also clears owner of 1x1 land's owned by msg.sender.
          /// @param quadMinted array of lands
          /// @param index array size
          /// @param numLandMinted number of lands transferred
          /// @param to recipient
          /// @param size The size of the new quad
          /// @param x The top left x coordinate of the new quad
          /// @param y The top left y coordinate of the new quad
          /// @param data extra data
          function _checkBatchReceiverAcceptQuadAndClearOwner(
              Land[] memory quadMinted,
              uint256 index,
              uint256 numLandMinted,
              address to,
              uint256 size,
              uint256 x,
              uint256 y,
              bytes memory data
          ) internal {
              // checks if to is a contract and supports ERC721_MANDATORY_RECEIVER interfaces.
              // if it doesn't it just clears the owner of 1x1 lands in quad(size, x, y)
              if (to.isContract() && _checkInterfaceWith10000Gas(to, ERC721_MANDATORY_RECEIVER)) {
                  // array to push minted 1x1 land
                  uint256[] memory idsToTransfer = new uint256[](numLandMinted);
                  // index of last land pushed in idsToTransfer array
                  uint256 transferIndex;
                  // array to push ids to be minted
                  uint256[] memory idsToMint = new uint256[]((size * size) - numLandMinted);
                  // index of last land pushed in idsToMint array
                  uint256 mintIndex;
                  // iterating over every 1x1 land in the quad to be pushed in the above arrays
                  for (uint256 i = 0; i < size * size; i++) {
                      uint256 id = _idInPath(i, size, x, y);
                      if (_isQuadMinted(quadMinted, Land({x: _getX(id), y: _getY(id), size: 1}), index)) {
                          // if land is in the quads already minted it just pushed in to the idsToTransfer array
                          idsToTransfer[transferIndex] = id;
                          transferIndex++;
                      } else if (address(uint160(_owners[id])) == msg.sender) {
                          _owners[id] = 0;
                          idsToTransfer[transferIndex] = id;
                          transferIndex++;
                      } else {
                          // else it is not owned by any one and and pushed in teh idsToMint array
                          idsToMint[mintIndex] = id;
                          mintIndex++;
                      }
                  }
                  // checking if "to" contact can handle ERC721 tokens
                  require(
                      _checkOnERC721BatchReceived(msg.sender, address(0), to, idsToMint, data),
                      "erc721 batch transfer rejected by to"
                  );
                  require(
                      _checkOnERC721BatchReceived(msg.sender, msg.sender, to, idsToTransfer, data),
                      "erc721 batch transfer rejected by to"
                  );
              } else {
                  for (uint256 i = 0; i < size * size; i++) {
                      uint256 id = _idInPath(i, size, x, y);
                      if (address(uint160(_owners[id])) == msg.sender) _owners[id] = 0;
                  }
              }
          }
          /// @param from current owner of the quad
          /// @param to destination
          /// @param size size of the quad
          /// @param x The top left x coordinate of the quad
          /// @param y The top left y coordinate of the quad
          function _transferQuad(
              address from,
              address to,
              uint256 size,
              uint256 x,
              uint256 y
          ) internal {
              _isValidQuad(size, x, y);
              if (size == 1) {
                  uint256 id1x1 = _getQuadId(LAYER_1x1, x, y);
                  address owner = _ownerOf(id1x1);
                  require(owner != address(0), "token does not exist");
                  require(owner == from, "not owner in _transferQuad");
                  _owners[id1x1] = uint256(to);
              } else {
                  _regroupQuad(from, to, Land({x: x, y: y, size: size}), true, size / 2);
              }
              for (uint256 i = 0; i < size * size; i++) {
                  emit Transfer(from, to, _idInPath(i, size, x, y));
              }
          }
          /// @dev checks if the quad is already minted compared to another quad size
          /// @param size size of the quad
          /// @param x The top left x coordinate of the quad
          /// @param y The top left y coordinate of the quad
          /// @param quadCompareSize size to compare with
          function _checkOwner(
              uint256 size,
              uint256 x,
              uint256 y,
              uint256 quadCompareSize
          ) internal view {
              (uint256 layer, , ) = _getQuadLayer(quadCompareSize);
              if (size <= quadCompareSize) {
                  // when the size of the quad is smaller than the quadCompareSize(size to be compared with),
                  // then it is checked if the bigger quad which encapsulates the quad to be minted
                  // of with size equals the quadCompareSize has been minted or not
                  require(
                      _owners[
                          _getQuadId(layer, (x / quadCompareSize) * quadCompareSize, (y / quadCompareSize) * quadCompareSize)
                      ] == 0,
                      "Already minted"
                  );
              } else {
                  // when the size is bigger than the quadCompare size the owner of all the smaller quads with size
                  // quadCompare size in the quad to be minted are checked if they are minted or not
                  uint256 toX = x + size;
                  uint256 toY = y + size;
                  for (uint256 xi = x; xi < toX; xi += quadCompareSize) {
                      for (uint256 yi = y; yi < toY; yi += quadCompareSize) {
                          require(_owners[_getQuadId(layer, xi, yi)] == 0, "Already minted");
                      }
                  }
              }
              quadCompareSize = quadCompareSize / 2;
              if (quadCompareSize >= 3) _checkOwner(size, x, y, quadCompareSize);
          }
          /// @dev checks the owner of land of token id 'id' to be 'from' and clears it
          /// @param from owner of the token
          /// @param tokenId token id
          /// @return if the address is the owner of the token
          function _checkAndClearLandOwner(address from, uint256 tokenId) internal returns (bool) {
              uint256 currentOwner = _owners[tokenId];
              if (currentOwner != 0) {
                  require(address(currentOwner) == from, "not owner");
                  _owners[tokenId] = 0;
                  return true;
              }
              return false;
          }
          /** @dev recursivly checks if the child quads are minted in land and push them to the quadMinted array.
           * if a child quad is minted in land such quads child quads will be skipped such that there is no
           * overlapping in quads which are minted. it clears the minted child quads owners.
           * @param land the stuct which has the size x and y co-ordinate of Quad to be checked
           * @param quadMinted array in which the minted child quad would be pushed
           * @param numLandMinted number of lands transferred
           * @param index index of last element of quadMinted array
           * @param quadCompareSize the size of the child quads to be checked.
           * @return the index of last quad pushed in quadMinted array and the total land already minted
           * @return the number of lands minted
           */
          function _checkAndClearOwner(
              Land memory land,
              Land[] memory quadMinted,
              uint256 numLandMinted,
              uint256 index,
              uint256 quadCompareSize
          ) internal returns (uint256, uint256) {
              (uint256 layer, , ) = _getQuadLayer(quadCompareSize);
              uint256 toX = land.x + land.size;
              uint256 toY = land.y + land.size;
              //Lopping around the Quad in land struct to check if the child quad are minted or not
              for (uint256 xi = land.x; xi < toX; xi += quadCompareSize) {
                  for (uint256 yi = land.y; yi < toY; yi += quadCompareSize) {
                      //checking if the child Quad is minted or not. i.e Checks if the quad is in the quadMinted array.
                      bool isQuadChecked = _isQuadMinted(quadMinted, Land({x: xi, y: yi, size: quadCompareSize}), index);
                      // if child quad is not already in the quadMinted array.
                      if (!isQuadChecked) {
                          uint256 id = _getQuadId(layer, xi, yi);
                          address owner = address(uint160(_owners[id]));
                          // owner of the child quad is checked to be owned by msg.sender else should not be owned by anyone.
                          if (owner == msg.sender) {
                              // if child quad is minted it would be pushed in quadMinted array.
                              quadMinted[index] = Land({x: xi, y: yi, size: quadCompareSize});
                              // index of quadMinted is increased
                              index++;
                              // total land minted is increase by the number if land of 1x1 in child quad
                              numLandMinted += quadCompareSize * quadCompareSize;
                              //owner is cleared
                              _owners[id] = 0;
                          } else {
                              require(owner == address(0), "Already minted");
                          }
                      }
                  }
              }
              // size of the child quad is set to be the next smaller child quad size (12 => 6 => 3)
              quadCompareSize = quadCompareSize / 2;
              // if child quad size is greater than 3 _checkAndClearOwner is checked for new child quads in the  quad in land struct.
              if (quadCompareSize >= 3)
                  (index, numLandMinted) = _checkAndClearOwner(land, quadMinted, numLandMinted, index, quadCompareSize);
              return (index, numLandMinted);
          }
          /// @dev checks if the Land's child quads are owned by the from address and clears all the previous owners
          /// if all the child quads are not owned by the "from" address then the owner of parent quad to the land
          /// is checked if owned by the "from" address. If from is the owner then land owner is set to "to" address
          /// @param from address of the previous owner
          /// @param to address of the new owner
          /// @param land the quad to be regrouped and transferred
          /// @param set for setting the new owner
          /// @param childQuadSize  size of the child quad to be checked for owner in the regrouping
          function _regroupQuad(
              address from,
              address to,
              Land memory land,
              bool set,
              uint256 childQuadSize
          ) internal returns (bool) {
              (uint256 layer, , uint256 childLayer) = _getQuadLayer(land.size);
              uint256 quadId = _getQuadId(layer, land.x, land.y);
              bool ownerOfAll = true;
              {
                  // double for loop iterates and checks owner of all the smaller quads in land
                  for (uint256 xi = land.x; xi < land.x + land.size; xi += childQuadSize) {
                      for (uint256 yi = land.y; yi < land.y + land.size; yi += childQuadSize) {
                          uint256 ownerChild;
                          bool ownAllIndividual;
                          if (childQuadSize < 3) {
                              // case when the smaller quad is 1x1,
                              ownAllIndividual = _checkAndClearLandOwner(from, _getQuadId(LAYER_1x1, xi, yi)) && ownerOfAll;
                          } else {
                              // recursively calling the _regroupQuad function to check the owner of child quads.
                              ownAllIndividual = _regroupQuad(
                                  from,
                                  to,
                                  Land({x: xi, y: yi, size: childQuadSize}),
                                  false,
                                  childQuadSize / 2
                              );
                              uint256 idChild = _getQuadId(childLayer, xi, yi);
                              ownerChild = _owners[idChild];
                              if (ownerChild != 0) {
                                  if (!ownAllIndividual) {
                                      // checking the owner of child quad
                                      require(ownerChild == uint256(from), "not owner of child Quad");
                                  }
                                  // clearing owner of child quad
                                  _owners[idChild] = 0;
                              }
                          }
                          // ownerOfAll should be true if "from" is owner of all the child quads iterated over
                          ownerOfAll = (ownAllIndividual || ownerChild != 0) && ownerOfAll;
                      }
                  }
              }
              // if set is true it check if the "from" is owner of all else checks for the owner of parent quad is
              // owned by "from" and sets the owner for the id of land to "to" address.
              if (set) {
                  if (!ownerOfAll) {
                      require(_ownerOfQuad(land.size, land.x, land.y) == from, "not owner of all sub quads nor parent quads");
                  }
                  _owners[quadId] = uint256(to);
                  return true;
              }
              return ownerOfAll;
          }
          /// @notice Goes through every token id of a quad id
          /// @param i ith token of the quad
          /// @param size size of the quad
          /// @param x The top left x coordinate of the quad
          /// @param y The top left y coordinate of the quad
          /// @return the "ith" token id of the quad
          function _idInPath(
              uint256 i,
              uint256 size,
              uint256 x,
              uint256 y
          ) internal pure returns (uint256) {
              uint256 row = i / size;
              if (row % 2 == 0) {
                  // allow ids to follow a path in a quad
                  return _getQuadId(LAYER_1x1, (x + (i % size)), (y + row));
              } else {
                  return _getQuadId(LAYER_1x1, (x + size) - (1 + (i % size)), (y + row));
              }
          }
          /// @param mintedLand array of lands
          /// @param quad quad to check
          /// @param index size of the array
          /// @return is the quad minted
          function _isQuadMinted(
              Land[] memory mintedLand,
              Land memory quad,
              uint256 index
          ) internal pure returns (bool) {
              for (uint256 i = 0; i < index; i++) {
                  Land memory land = mintedLand[i];
                  if (
                      land.size > quad.size &&
                      quad.x >= land.x &&
                      quad.x < land.x + land.size &&
                      quad.y >= land.y &&
                      quad.y < land.y + land.size
                  ) {
                      return true;
                  }
              }
              return false;
          }
          /// @param id token id
          /// @return the x coordinate
          function _getX(uint256 id) internal pure returns (uint256) {
              return (id & ~LAYER) % GRID_SIZE;
          }
          /// @param id token id
          /// @return the y coordinate
          function _getY(uint256 id) internal pure returns (uint256) {
              return (id & ~LAYER) / GRID_SIZE;
          }
          /// @param size of the quad
          /// @return layer the layer associated to that quad size
          /// @return parentSize size of the parent quad
          /// @return childLayer layer of the child quad size
          function _getQuadLayer(uint256 size)
              internal
              pure
              returns (
                  uint256 layer,
                  uint256 parentSize,
                  uint256 childLayer
              )
          {
              if (size == 1) {
                  layer = LAYER_1x1;
                  parentSize = 3;
              } else if (size == 3) {
                  layer = LAYER_3x3;
                  parentSize = 6;
              } else if (size == 6) {
                  layer = LAYER_6x6;
                  parentSize = 12;
                  childLayer = LAYER_3x3;
              } else if (size == 12) {
                  layer = LAYER_12x12;
                  parentSize = 24;
                  childLayer = LAYER_6x6;
              } else if (size == 24) {
                  layer = LAYER_24x24;
                  childLayer = LAYER_12x12;
              } else {
                  require(false, "Invalid size");
              }
          }
          /// @param layer of the quad size
          /// @param x coordinate of the quad
          /// @param y coordinate of the quad
          /// @return the quad id
          function _getQuadId(
              uint256 layer,
              uint256 x,
              uint256 y
          ) internal pure returns (uint256) {
              return layer + x + y * GRID_SIZE;
          }
          /// @param size of the quad
          /// @param x coordinate of the quad
          /// @param y coordinate of the quad
          /// @return address of the owner of the quad
          function _ownerOfQuad(
              uint256 size,
              uint256 x,
              uint256 y
          ) internal view returns (address) {
              (uint256 layer, uint256 parentSize, ) = _getQuadLayer(size);
              address owner = address(_owners[_getQuadId(layer, (x / size) * size, (y / size) * size)]);
              if (owner != address(0)) {
                  return owner;
              } else if (size < 24) {
                  return _ownerOfQuad(parentSize, x, y);
              }
              return address(0);
          }
          /// @param id quad id
          /// @return size of the quad
          /// @return x coordinate
          /// @return y coordinate
          function _getQuadById(uint256 id)
              internal
              pure
              returns (
                  uint256 size,
                  uint256 x,
                  uint256 y
              )
          {
              x = _getX(id);
              y = _getY(id);
              uint256 layer = id & LAYER;
              if (layer == LAYER_1x1) {
                  size = 1;
              } else if (layer == LAYER_3x3) {
                  size = 3;
              } else if (layer == LAYER_6x6) {
                  size = 6;
              } else if (layer == LAYER_12x12) {
                  size = 12;
              } else if (layer == LAYER_24x24) {
                  size = 24;
              } else {
                  require(false, "Invalid token id");
              }
          }
          /// @param id quad id
          /// @return address of the owner
          function _ownerOf(uint256 id) internal view returns (address) {
              require(id & LAYER == 0, "Invalid token id");
              (uint256 size, uint256 x, uint256 y) = _getQuadById(id);
              require(x % size == 0, "x coordinate: Invalid token id");
              require(y % size == 0, "y coordinate: Invalid token id");
              return _ownerOfQuad(size, x, y);
          }
          /// @param id token id
          /// @return owner owner of the token
          /// @return operatorEnabled is operator enabled
          function _ownerAndOperatorEnabledOf(uint256 id) internal view returns (address owner, bool operatorEnabled) {
              require(id & LAYER == 0, "Invalid token id");
              uint256 x = _getX(id);
              uint256 y = _getY(id);
              uint256 owner1x1 = _owners[id];
              if (owner1x1 != 0) {
                  owner = address(owner1x1);
                  operatorEnabled = (owner1x1 / 2**255) == 1;
              } else {
                  owner = _ownerOfQuad(3, (x * 3) / 3, (y * 3) / 3);
                  operatorEnabled = false;
              }
          }
      }
      // SPDX-License-Identifier: MIT
      /* solhint-disable no-empty-blocks */
      pragma solidity 0.5.9;
      import {LandBaseTokenV3} from "./Land/erc721/LandBaseTokenV3.sol";
      import {OperatorFiltererUpgradeable, IOperatorFilterRegistry} from "./OperatorFilterer/contracts/upgradeable/OperatorFiltererUpgradeable.sol";
      /**
       * @title LandV3
       * @author The Sandbox
       * @notice LAND contract
       * @dev LAND contract implements ERC721, quad and marketplace filtering functionalities
       */
      contract LandV3 is LandBaseTokenV3, OperatorFiltererUpgradeable {
          event OperatorRegistrySet(address indexed registry);
          /**
           * @notice Return the name of the token contract
           * @return The name of the token contract
           */
          function name() external pure returns (string memory) {
              return "Sandbox's LANDs";
          }
          /**
           * @notice Return the symbol of the token contract
           * @return The symbol of the token contract
           */
          function symbol() external pure returns (string memory) {
              return "LAND";
          }
          // solium-disable-next-line security/no-assign-params
          function uint2str(uint256 _i) internal pure returns (string memory) {
              if (_i == 0) {
                  return "0";
              }
              uint256 j = _i;
              uint256 len;
              while (j != 0) {
                  len++;
                  j /= 10;
              }
              bytes memory bstr = new bytes(len);
              uint256 k = len - 1;
              while (_i != 0) {
                  bstr[k--] = byte(uint8(48 + (_i % 10)));
                  _i /= 10;
              }
              return string(bstr);
          }
          /**
           * @notice Return the URI of a specific token
           * @param id The id of the token
           * @return The URI of the token
           */
          function tokenURI(uint256 id) public view returns (string memory) {
              require(_ownerOf(id) != address(0), "LandV3: Id does not exist");
              return string(abi.encodePacked("https://api.sandbox.game/lands/", uint2str(id), "/metadata.json"));
          }
          /**
           * @notice Check if the contract supports an interface
           * 0x01ffc9a7 is ERC-165
           * 0x80ac58cd is ERC-721
           * 0x5b5e139f is ERC-721 metadata
           * @param id The id of the interface
           * @return True if the interface is supported
           */
          function supportsInterface(bytes4 id) external pure returns (bool) {
              return id == 0x01ffc9a7 || id == 0x80ac58cd || id == 0x5b5e139f;
          }
          /// @notice This function is used to register Land contract on the Operator Filterer Registry of Opensea.can only be called by admin.
          /// @dev used to register contract and subscribe to the subscriptionOrRegistrantToCopy's black list.
          /// @param subscriptionOrRegistrantToCopy registration address of the list to subscribe.
          /// @param subscribe bool to signify subscription "true"" or to copy the list "false".
          function register(address subscriptionOrRegistrantToCopy, bool subscribe) external onlyAdmin {
              require(subscriptionOrRegistrantToCopy != address(0), "LandV3: subscription can't be zero address");
              _register(subscriptionOrRegistrantToCopy, subscribe);
          }
          /// @notice sets filter registry address deployed in test
          /// @param registry the address of the registry
          function setOperatorRegistry(address registry) external onlyAdmin {
              operatorFilterRegistry = IOperatorFilterRegistry(registry);
              emit OperatorRegistrySet(registry);
          }
          /**
           * @notice Approve an operator to spend tokens on the sender behalf
           * @param sender The address giving the approval
           * @param operator The address receiving the approval
           * @param id The id of the token
           */
          function approveFor(
              address sender,
              address operator,
              uint256 id
          ) public onlyAllowedOperatorApproval(operator) {
              super.approveFor(sender, operator, id);
          }
          /**
           * @notice Set the approval for an operator to manage all the tokens of the sender
           * @param operator The address receiving the approval
           * @param approved The determination of the approval
           */
          function setApprovalForAll(address operator, bool approved) public onlyAllowedOperatorApproval(operator) {
              super.setApprovalForAll(operator, approved);
          }
          /**
           * @notice Set the approval for an operator to manage all the tokens of the sender
           * @param sender The address giving the approval
           * @param operator The address receiving the approval
           * @param approved The determination of the approval
           */
          function setApprovalForAllFor(
              address sender,
              address operator,
              bool approved
          ) public onlyAllowedOperatorApproval(operator) {
              super.setApprovalForAllFor(sender, operator, approved);
          }
          /**
           * @notice Approve an operator to spend tokens on the sender behalf
           * @param operator The address receiving the approval
           * @param id The id of the token
           */
          function approve(address operator, uint256 id) public onlyAllowedOperatorApproval(operator) {
              super.approve(operator, id);
          }
          /**
           * @notice Transfer a token between 2 addresses
           * @param from The sender of the token
           * @param to The recipient of the token
           * @param id The id of the token
           */
          function transferFrom(
              address from,
              address to,
              uint256 id
          ) public onlyAllowedOperator(from) {
              super.transferFrom(from, to, id);
          }
          /**
           * @notice Transfer a token between 2 addresses letting the receiver knows of the transfer
           * @param from The sender of the token
           * @param to The recipient of the token
           * @param id The id of the token
           * @param data Additional data
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              bytes memory data
          ) public onlyAllowedOperator(from) {
              super.safeTransferFrom(from, to, id, data);
          }
          /**
           * @notice Transfer a token between 2 addresses letting the receiver knows of the transfer
           * @param from The send of the token
           * @param to The recipient of the token
           * @param id The id of the token
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id
          ) external onlyAllowedOperator(from) {
              super.safeTransferFrom(from, to, id, "");
          }
      }
      //SPDX-License-Identifier: MIT
      pragma solidity 0.5.9;
      import {IOperatorFilterRegistry} from "../../interfaces/IOperatorFilterRegistry.sol";
      import {AddressUtils} from "../../../contracts_common/Libraries/AddressUtils.sol";
      /// @title OperatorFiltererUpgradeable
      /// @author The Sandbox
      /// @notice This contract would subscibe or copy or just to the subscription provided or just register to default subscription list
      /// @dev This contract is the upgradeable version of the OpenSea implementation https://github.com/ProjectOpenSea/operator-filter-registry/blob/main/src/OperatorFilterer.sol and adapted to the 0.5.9 solidity version
      contract OperatorFiltererUpgradeable {
          using AddressUtils for address;
          IOperatorFilterRegistry public operatorFilterRegistry;
          event ContractRegistered(address indexed subscriptionOrRegistrant, bool subscribe);
          /**
           * @notice Register this contract into the registry
           * @param subscriptionOrRegistrantToCopy address to subscribe or copy entries from
           * @param subscribe should it subscribe
           */
          function _register(address subscriptionOrRegistrantToCopy, bool subscribe) internal {
              // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
              // will not revert, but the contract will need to be registered with the registry once it is deployed in
              // order for the modifier to filter addresses.
              if (address(operatorFilterRegistry).isContract()) {
                  if (!operatorFilterRegistry.isRegistered(address(this))) {
                      if (subscribe) {
                          operatorFilterRegistry.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                      } else {
                          if (subscriptionOrRegistrantToCopy != address(0)) {
                              operatorFilterRegistry.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                          } else {
                              operatorFilterRegistry.register(address(this));
                          }
                      }
                  }
              }
              emit ContractRegistered(subscriptionOrRegistrantToCopy, subscribe);
          }
          modifier onlyAllowedOperator(address from) {
              // Check registry code length to facilitate testing in environments without a deployed registry.
              if (address(operatorFilterRegistry).isContract()) {
                  // Allow spending tokens from addresses with balance
                  // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                  // from an EOA.
                  if (from == msg.sender) {
                      _;
                      return;
                  }
                  if (!operatorFilterRegistry.isOperatorAllowed(address(this), msg.sender)) {
                      revert("Operator Not Allowed");
                  }
              }
              _;
          }
          modifier onlyAllowedOperatorApproval(address operator) {
              // Check registry code length to facilitate testing in environments without a deployed registry.
              if (address(operatorFilterRegistry).isContract()) {
                  if (!operatorFilterRegistry.isOperatorAllowed(address(this), operator)) {
                      revert("Operator Not Allowed");
                  }
              }
              _;
          }
      }
      //SPDX-License-Identifier: MIT
      pragma solidity 0.5.9;
      /**
       * @title IOperatorFilterRegistry
       * @author OpenSea
       * @notice Interface of the operator filter registry
       * @dev This interface comes from OpenSea https://github.com/ProjectOpenSea/operator-filter-registry/blob/main/src/IOperatorFilterRegistry.sol and adapted to the 0.5.9 solidity version
       */
      interface IOperatorFilterRegistry {
          /**
           * @notice Check if the operator is allowed for the given registrant
           * @param registrant address of the registrant
           * @param operator operator address to check
           * @return is the operator allowed
           */
          function isOperatorAllowed(address registrant, address operator) external view returns (bool);
          /**
           * @notice Register a new address
           * @param registrant address to register
           */
          function register(address registrant) external;
          /**
           * @notice Register a new address & subscribe to an address
           * @param registrant address of the registrant
           * @param subscription address where the registrant is subscribed to
           */
          function registerAndSubscribe(address registrant, address subscription) external;
          /**
           * @notice Register and copy entries of another registrant
           * @param registrant address of the registrant
           * @param registrantToCopy address to copy from
           */
          function registerAndCopyEntries(address registrant, address registrantToCopy) external;
          /**
           * @notice update the operator for a registrant
           * @param registrant address of the registrant
           * @param operator operator to be updated
           * @param filtered is it filtered
           */
          function updateOperator(
              address registrant,
              address operator,
              bool filtered
          ) external;
          /**
           * @notice Update operators for a registrant
           * @param registrant address of the registrant
           * @param operators addresses of the operators
           * @param filtered is it filtered
           */
          function updateOperators(
              address registrant,
              address[] calldata operators,
              bool filtered
          ) external;
          /**
           * @notice Update code hash
           * @param registrant address of the registrant
           * @param codehash code hash
           * @param filtered is it filtered
           */
          function updateCodeHash(
              address registrant,
              bytes32 codehash,
              bool filtered
          ) external;
          /**
           * @notice Update code hashes
           * @param registrant address of the registrant
           * @param codeHashes code hashes
           * @param filtered is it filtered
           */
          function updateCodeHashes(
              address registrant,
              bytes32[] calldata codeHashes,
              bool filtered
          ) external;
          /**
           * @notice Subscribe a registrant
           * @param registrant address of the registrant
           * @param registrantToSubscribe address to subscribe with
           */
          function subscribe(address registrant, address registrantToSubscribe) external;
          /**
           * @notice Unsubscribe a registrant
           * @param registrant address of the registrant
           * @param copyExistingEntries copy existing entries
           */
          function unsubscribe(address registrant, bool copyExistingEntries) external;
          /**
           * @notice Get the subscription of an address
           * @param addr address to check
           * @return the registrant address
           */
          function subscriptionOf(address addr) external returns (address registrant);
          /**
           * @notice Get the subscribers of the registrant
           * @param registrant address of the registrant
           * @return the subscribers addresses
           */
          function subscribers(address registrant) external returns (address[] memory);
          /**
           * @notice Get a specific subscriber
           * @param registrant address of the registrant
           * @param index index to check
           * @return the ith subscriber of the registrant
           */
          function subscriberAt(address registrant, uint256 index) external returns (address);
          /**
           * @notice Copy the entries of a registrant
           * @param registrant address of the registrant
           * @param registrantToCopy address to copy
           */
          function copyEntriesOf(address registrant, address registrantToCopy) external;
          /**
           * @notice Is a registrant filtered
           * @param registrant address of the registrant
           * @param operator operator address to check
           * @return is it filtered
           */
          function isOperatorFiltered(address registrant, address operator) external returns (bool);
          /**
           * @notice Is the code hash of an operator filtered
           * @param registrant address of the registrant
           * @param operatorWithCode operator address to check
           * @return is it filtered
           */
          function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
          /**
           * @notice Is the code hash filtered
           * @param registrant address of the registrant
           * @param codeHash code hash
           * @return is it filtered
           */
          function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
          /**
           * @notice Get the filtered operators
           * @param addr address to check
           * @return filtered operators
           */
          function filteredOperators(address addr) external returns (address[] memory);
          /**
           * @notice Get the filtered code hashes
           * @param addr address to check
           * @return filtered code hashes
           */
          function filteredCodeHashes(address addr) external returns (bytes32[] memory);
          /**
           * @notice Get a specific operator
           * @param registrant address of the registrant
           * @param index index to check
           * @return address of the operator
           */
          function filteredOperatorAt(address registrant, uint256 index) external returns (address);
          /**
           * @notice Get the ith filtered code hash
           * @param registrant address of the registrant
           * @param index index to check
           * @return the code hash
           */
          function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
          /**
           * @notice Is the address registered
           * @param addr address to check
           * @return is it registered
           */
          function isRegistered(address addr) external returns (bool);
          /**
           * @notice Get the code hash for this address
           * @param addr address to check
           * @return the code hash
           */
          function codeHashOf(address addr) external returns (bytes32);
      }
      

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