ETH Price: $1,786.48 (+4.81%)

Contract

0x16B7F92cdfc9e1aAf30fc7af8914d539B5340a55
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions and > 10 Token Transfers found.

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
65637662024-07-25 19:55:47271 days ago1721937347
0x16B7F92c...9B5340a55
0 ETH
65637662024-07-25 19:55:47271 days ago1721937347
0x16B7F92c...9B5340a55
0.00001 ETH
65637662024-07-25 19:55:47271 days ago1721937347
0x16B7F92c...9B5340a55
0.00001 ETH
54651222024-06-30 9:34:19297 days ago1719740059
0x16B7F92c...9B5340a55
0.00002331 ETH
54651222024-06-30 9:34:19297 days ago1719740059
0x16B7F92c...9B5340a55
0.000777 ETH
54651222024-06-30 9:34:19297 days ago1719740059
0x16B7F92c...9B5340a55
0.00080031 ETH
53148832024-06-26 22:06:21300 days ago1719439581
0x16B7F92c...9B5340a55
0.0001 ETH
53148832024-06-26 22:06:21300 days ago1719439581
0x16B7F92c...9B5340a55
0.0001 ETH
50518952024-06-20 20:00:05306 days ago1718913605
0x16B7F92c...9B5340a55
0.000009 ETH
50518952024-06-20 20:00:05306 days ago1718913605
0x16B7F92c...9B5340a55
0.0003 ETH
50518952024-06-20 20:00:05306 days ago1718913605
0x16B7F92c...9B5340a55
0.000309 ETH
44302952024-06-06 10:40:05321 days ago1717670405
0x16B7F92c...9B5340a55
0.00006 ETH
44302952024-06-06 10:40:05321 days ago1717670405
0x16B7F92c...9B5340a55
0.002 ETH
44302952024-06-06 10:40:05321 days ago1717670405
0x16B7F92c...9B5340a55
0.00206 ETH
43594532024-06-04 19:18:41322 days ago1717528721
0x16B7F92c...9B5340a55
0.000066 ETH
43594532024-06-04 19:18:41322 days ago1717528721
0x16B7F92c...9B5340a55
0.0022 ETH
43594532024-06-04 19:18:41322 days ago1717528721
0x16B7F92c...9B5340a55
0.002266 ETH
43592972024-06-04 19:13:29322 days ago1717528409
0x16B7F92c...9B5340a55
0.000063 ETH
43592972024-06-04 19:13:29322 days ago1717528409
0x16B7F92c...9B5340a55
0.0021 ETH
43592972024-06-04 19:13:29322 days ago1717528409
0x16B7F92c...9B5340a55
0.002163 ETH
42596302024-06-02 11:51:15325 days ago1717329075
0x16B7F92c...9B5340a55
0.000066 ETH
42596302024-06-02 11:51:15325 days ago1717329075
0x16B7F92c...9B5340a55
0.0022 ETH
42596302024-06-02 11:51:15325 days ago1717329075
0x16B7F92c...9B5340a55
0.002266 ETH
42314182024-06-01 20:10:51325 days ago1717272651
0x16B7F92c...9B5340a55
0.000066 ETH
42314182024-06-01 20:10:51325 days ago1717272651
0x16B7F92c...9B5340a55
0.0022 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SeaportV15Module

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 129 : SeaportV15Module.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ISeaport} from "../../../interfaces/ISeaport.sol";

// Notes on the Seaport module:
// - supports filling listings (both ERC721/ERC1155)
// - supports filling offers (both ERC721/ERC1155)

contract SeaportV15Module is BaseExchangeModule {
  // --- Structs ---

  struct SeaportETHListingWithPrice {
    ISeaport.AdvancedOrder order;
    uint256 price;
  }

  struct SeaportPrivateListingWithPrice {
    ISeaport.AdvancedOrder[] orders;
    ISeaport.Fulfillment[] fulfillments;
    uint256 price;
  }

  // --- Fields ---

  ISeaport public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = ISeaport(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    ISeaport.AdvancedOrder calldata order,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(
        order,
        new ISeaport.CriteriaResolver[](0),
        params.fillTo,
        params.amount
      )
      : _fillSingleOrder(order, new ISeaport.CriteriaResolver[](0), params.fillTo, params.amount);
  }

  // --- Single ERC20 listing ---

  function acceptERC20Listing(
    ISeaport.AdvancedOrder calldata order,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(
        order,
        new ISeaport.CriteriaResolver[](0),
        params.fillTo,
        0
      )
      : _fillSingleOrder(order, new ISeaport.CriteriaResolver[](0), params.fillTo, 0);
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    SeaportETHListingWithPrice[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillSingleOrderWithRevertIfIncomplete(
          orders[i].order,
          criteriaResolvers,
          params.fillTo,
          orders[i].price
        );

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillSingleOrder(orders[i].order, criteriaResolvers, params.fillTo, orders[i].price);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Multiple ERC20 listings ---

  function acceptERC20Listings(
    ISeaport.AdvancedOrder[] calldata orders,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillSingleOrderWithRevertIfIncomplete(orders[i], criteriaResolvers, params.fillTo, 0);

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillSingleOrder(orders[i], criteriaResolvers, params.fillTo, 0);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Multiple ETH private listings ---

  function acceptETHPrivateListings(
    SeaportPrivateListingWithPrice[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillPrivateOrderWithRevertIfIncomplete(
          orders[i].orders,
          criteriaResolvers,
          orders[i].fulfillments,
          params.fillTo,
          orders[i].price
        );

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillPrivateOrder(
          orders[i].orders,
          criteriaResolvers,
          orders[i].fulfillments,
          params.fillTo,
          orders[i].price
        );

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Multiple Private ERC20 Private listings ---

  function acceptERC20PrivateListings(
    SeaportPrivateListingWithPrice[] calldata orders,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillPrivateOrderWithRevertIfIncomplete(
          orders[i].orders,
          criteriaResolvers,
          orders[i].fulfillments,
          params.fillTo,
          0
        );

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillPrivateOrder(
          orders[i].orders,
          criteriaResolvers,
          orders[i].fulfillments,
          params.fillTo,
          0
        );

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Single ERC721 offer ---

  function acceptERC721Offer(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    // Extract the ERC721 token from the consideration items
    ISeaport.ConsiderationItem calldata nftItem = order.parameters.consideration[0];
    if (
      nftItem.itemType != ISeaport.ItemType.ERC721 &&
      nftItem.itemType != ISeaport.ItemType.ERC721_WITH_CRITERIA
    ) {
      revert WrongParams();
    }
    IERC721 nftToken = IERC721(nftItem.token);

    // Extract the payment token from the offer items
    ISeaport.OfferItem calldata paymentItem = order.parameters.offer[0];
    IERC20 paymentToken = IERC20(paymentItem.token);

    // Approve the exchange if needed
    _approveERC721IfNeeded(nftToken, address(EXCHANGE));
    _approveERC20IfNeeded(paymentToken, address(EXCHANGE), type(uint256).max);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(order, criteriaResolvers, address(this), 0)
      : _fillSingleOrder(order, criteriaResolvers, address(this), 0);

    uint256 identifier = nftItem.itemType == ISeaport.ItemType.ERC721
      ? nftItem.identifierOrCriteria
      : criteriaResolvers[0].identifier;

    // Pay fees
    if (nftToken.ownerOf(identifier) != address(this)) {
      // Only pay fees if the fill was successful
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, paymentToken);

        unchecked {
          ++i;
        }
      }
    }

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, nftToken, identifier);

    // Forward any left payment to the specified receiver
    _sendAllERC20(params.fillTo, paymentToken);
  }

  // --- Single ERC1155 offer ---

  function acceptERC1155Offer(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    // Extract the ERC1155 token from the consideration items
    ISeaport.ConsiderationItem calldata nftItem = order.parameters.consideration[0];
    if (
      nftItem.itemType != ISeaport.ItemType.ERC1155 &&
      nftItem.itemType != ISeaport.ItemType.ERC1155_WITH_CRITERIA
    ) {
      revert WrongParams();
    }
    IERC1155 nftToken = IERC1155(nftItem.token);

    // Extract the payment token from the offer items
    ISeaport.OfferItem calldata paymentItem = order.parameters.offer[0];
    IERC20 paymentToken = IERC20(paymentItem.token);

    // Approve the exchange if needed
    _approveERC1155IfNeeded(nftToken, address(EXCHANGE));
    _approveERC20IfNeeded(paymentToken, address(EXCHANGE), type(uint256).max);

    uint256 identifier = nftItem.itemType == ISeaport.ItemType.ERC1155
      ? nftItem.identifierOrCriteria
      : criteriaResolvers[0].identifier;

    uint256 balanceBefore = nftToken.balanceOf(address(this), identifier);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(order, criteriaResolvers, address(this), 0)
      : _fillSingleOrder(order, criteriaResolvers, address(this), 0);

    uint256 balanceAfter = nftToken.balanceOf(address(this), identifier);

    // Pay fees
    uint256 amountFilled = balanceBefore - balanceAfter;
    if (amountFilled > 0) {
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(
          fee.recipient,
          // Only pay fees for the amount that was actually filled
          (fee.amount * amountFilled) / order.numerator,
          paymentToken
        );

        unchecked {
          ++i;
        }
      }
    }

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, nftToken, identifier);

    // Forward any left payment to the specified receiver
    _sendAllERC20(params.fillTo, paymentToken);
  }

  // --- Generic handler (used for Seaport-based approvals) ---

  function matchOrders(
    ISeaport.Order[] calldata orders,
    ISeaport.Fulfillment[] calldata fulfillments
  ) external nonReentrant {
    // We don't perform any kind of input or return value validation,
    // so this function should be used with precaution - the official
    // way to use it is only for Seaport-based approvals
    EXCHANGE.matchOrders(orders, fulfillments);
  }

  // --- ERC1271 ---

  function isValidSignature(bytes32, bytes memory) external pure returns (bytes4) {
    // Needed for filling private listings
    return this.isValidSignature.selector;
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  // NOTE: In lots of cases, Seaport will not revert if fills were not
  // fully executed. An example of that is partial filling, which will
  // successfully fill any amount that is still available (including a
  // zero amount). One way to ensure that we revert in case of partial
  // executions is to check the order's filled amount before and after
  // we trigger the fill (we can use Seaport's `getOrderStatus` method
  // to check). Since this can be expensive in terms of gas, we have a
  // separate method variant to be called when reverts are enabled.

  function _fillSingleOrder(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    address receiver,
    uint256 value
  ) internal {
    // Execute the fill
    try
      EXCHANGE.fulfillAdvancedOrder{value: value}(order, criteriaResolvers, bytes32(0), receiver)
    {} catch {}
  }

  function _fillSingleOrderWithRevertIfIncomplete(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    address receiver,
    uint256 value
  ) internal {
    // Cache the order's hash
    bytes32 orderHash = _getOrderHash(order.parameters);

    // Before filling, get the order's filled amount
    uint256 beforeFilledAmount = _getFilledAmount(orderHash, order.denominator);

    // Execute the fill
    bool success;
    try
      EXCHANGE.fulfillAdvancedOrder{value: value}(order, criteriaResolvers, bytes32(0), receiver)
    returns (bool fulfilled) {
      success = fulfilled;
    } catch {
      revert UnsuccessfulFill();
    }

    if (!success) {
      revert UnsuccessfulFill();
    } else {
      // After successfully filling, get the order's filled amount
      uint256 afterFilledAmount = _getFilledAmount(orderHash, order.denominator);

      // Make sure the amount filled as part of this call is correct
      if (afterFilledAmount - beforeFilledAmount != order.numerator) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _fillPrivateOrder(
    ISeaport.AdvancedOrder[] calldata advancedOrders,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    ISeaport.Fulfillment[] memory fulfillments,
    address receiver,
    uint256 value
  ) internal {
    // Execute the fill
    try
      EXCHANGE.matchAdvancedOrders{value: value}(
        advancedOrders,
        criteriaResolvers,
        fulfillments,
        receiver
      )
    {} catch {}
  }

  function _fillPrivateOrderWithRevertIfIncomplete(
    ISeaport.AdvancedOrder[] calldata advancedOrders,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    ISeaport.Fulfillment[] memory fulfillments,
    address receiver,
    uint256 value
  ) internal {
    // Execute the fill
    try
      EXCHANGE.matchAdvancedOrders{value: value}(
        advancedOrders,
        criteriaResolvers,
        fulfillments,
        receiver
      )
    {} catch {
      revert UnsuccessfulFill();
    }
  }

  function _getOrderHash(
    // Must use `memory` instead of `calldata` for the below cast
    ISeaport.OrderParameters memory orderParameters
  ) internal view returns (bytes32 orderHash) {
    // `OrderParameters` and `OrderComponents` share the exact same
    // fields, apart from the last one, so here we simply treat the
    // `orderParameters` argument as `OrderComponents` and then set
    // the last field to the correct data
    ISeaport.OrderComponents memory orderComponents;
    assembly {
      orderComponents := orderParameters
    }
    orderComponents.counter = EXCHANGE.getCounter(orderParameters.offerer);

    orderHash = EXCHANGE.getOrderHash(orderComponents);
  }

  function _getFilledAmount(
    bytes32 orderHash,
    uint256 adjustedTotalSize
  ) internal view returns (uint256 adjustedTotalFilled) {
    (, , uint256 totalFilled, uint256 totalSize) = EXCHANGE.getOrderStatus(orderHash);
    adjustedTotalFilled = totalSize > 0 ? (totalFilled * adjustedTotalSize) / totalSize : 0;
  }
}

File 2 of 129 : OwnableBasic.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "./OwnablePermissions.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

abstract contract OwnableBasic is OwnablePermissions, Ownable {
    function _requireCallerIsContractOwner() internal view virtual override {
        _checkOwner();
    }
}

File 3 of 129 : OwnablePermissions.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/Context.sol";

abstract contract OwnablePermissions is Context {
    function _requireCallerIsContractOwner() internal view virtual;
}

File 4 of 129 : ERC721C.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../utils/CreatorTokenBase.sol";
import "../token/erc721/ERC721OpenZeppelin.sol";

/**
 * @title ERC721C
 * @author Limit Break, Inc.
 * @notice Extends OpenZeppelin's ERC721 implementation with Creator Token functionality, which
 *         allows the contract owner to update the transfer validation logic by managing a security policy in
 *         an external transfer validation security policy registry.  See {CreatorTokenTransferValidator}.
 */
abstract contract ERC721C is ERC721OpenZeppelin, CreatorTokenBase {

    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(ICreatorToken).interfaceId || super.supportsInterface(interfaceId);
    }

    /// @dev Ties the open-zeppelin _beforeTokenTransfer hook to more granular transfer validation logic
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize) internal virtual override {
        for (uint256 i = 0; i < batchSize;) {
            _validateBeforeTransfer(from, to, firstTokenId + i);
            unchecked {
                ++i;
            }
        }
    }

    /// @dev Ties the open-zeppelin _afterTokenTransfer hook to more granular transfer validation logic
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize) internal virtual override {
        for (uint256 i = 0; i < batchSize;) {
            _validateAfterTransfer(from, to, firstTokenId + i);
            unchecked {
                ++i;
            }
        }
    }
}

/**
 * @title ERC721CInitializable
 * @author Limit Break, Inc.
 * @notice Initializable implementation of ERC721C to allow for EIP-1167 proxy clones.
 */
abstract contract ERC721CInitializable is ERC721OpenZeppelinInitializable, CreatorTokenBase {
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(ICreatorToken).interfaceId || super.supportsInterface(interfaceId);
    }

    /// @dev Ties the open-zeppelin _beforeTokenTransfer hook to more granular transfer validation logic
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize) internal virtual override {
        for (uint256 i = 0; i < batchSize;) {
            _validateBeforeTransfer(from, to, firstTokenId + i);
            unchecked {
                ++i;
            }
        }
    }

    /// @dev Ties the open-zeppelin _afterTokenTransfer hook to more granular transfer validation logic
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize) internal virtual override {
        for (uint256 i = 0; i < batchSize;) {
            _validateAfterTransfer(from, to, firstTokenId + i);
            unchecked {
                ++i;
            }
        }
    }
}

File 5 of 129 : ICreatorToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../interfaces/ICreatorTokenTransferValidator.sol";

interface ICreatorToken {
    event TransferValidatorUpdated(address oldValidator, address newValidator);

    function getTransferValidator() external view returns (ICreatorTokenTransferValidator);
    function getSecurityPolicy() external view returns (CollectionSecurityPolicy memory);
    function getWhitelistedOperators() external view returns (address[] memory);
    function getPermittedContractReceivers() external view returns (address[] memory);
    function isOperatorWhitelisted(address operator) external view returns (bool);
    function isContractReceiverPermitted(address receiver) external view returns (bool);
    function isTransferAllowed(address caller, address from, address to) external view returns (bool);
}

File 6 of 129 : ICreatorTokenTransferValidator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./IEOARegistry.sol";
import "./ITransferSecurityRegistry.sol";
import "./ITransferValidator.sol";

interface ICreatorTokenTransferValidator is ITransferSecurityRegistry, ITransferValidator, IEOARegistry {}

File 7 of 129 : IEOARegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IEOARegistry is IERC165 {
    function isVerifiedEOA(address account) external view returns (bool);
}

File 8 of 129 : ITransferSecurityRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../utils/TransferPolicy.sol";

interface ITransferSecurityRegistry {
    event AddedToAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
    event CreatedAllowlist(AllowlistTypes indexed kind, uint256 indexed id, string indexed name);
    event ReassignedAllowlistOwnership(AllowlistTypes indexed kind, uint256 indexed id, address indexed newOwner);
    event RemovedFromAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
    event SetAllowlist(AllowlistTypes indexed kind, address indexed collection, uint120 indexed id);
    event SetTransferSecurityLevel(address indexed collection, TransferSecurityLevels level);

    function createOperatorWhitelist(string calldata name) external returns (uint120);
    function createPermittedContractReceiverAllowlist(string calldata name) external returns (uint120);
    function reassignOwnershipOfOperatorWhitelist(uint120 id, address newOwner) external;
    function reassignOwnershipOfPermittedContractReceiverAllowlist(uint120 id, address newOwner) external;
    function renounceOwnershipOfOperatorWhitelist(uint120 id) external;
    function renounceOwnershipOfPermittedContractReceiverAllowlist(uint120 id) external;
    function setTransferSecurityLevelOfCollection(address collection, TransferSecurityLevels level) external;
    function setOperatorWhitelistOfCollection(address collection, uint120 id) external;
    function setPermittedContractReceiverAllowlistOfCollection(address collection, uint120 id) external;
    function addOperatorToWhitelist(uint120 id, address operator) external;
    function addPermittedContractReceiverToAllowlist(uint120 id, address receiver) external;
    function removeOperatorFromWhitelist(uint120 id, address operator) external;
    function removePermittedContractReceiverFromAllowlist(uint120 id, address receiver) external;
    function getCollectionSecurityPolicy(address collection) external view returns (CollectionSecurityPolicy memory);
    function getWhitelistedOperators(uint120 id) external view returns (address[] memory);
    function getPermittedContractReceivers(uint120 id) external view returns (address[] memory);
    function isOperatorWhitelisted(uint120 id, address operator) external view returns (bool);
    function isContractReceiverPermitted(uint120 id, address receiver) external view returns (bool);
}

File 9 of 129 : ITransferValidator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../utils/TransferPolicy.sol";

interface ITransferValidator {
    function applyCollectionTransferPolicy(address caller, address from, address to) external view;
}

File 10 of 129 : ERC721OpenZeppelin.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "../../access/OwnablePermissions.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

abstract contract ERC721OpenZeppelinBase is ERC721 {

    // Token name
    string internal _contractName;

    // Token symbol
    string internal _contractSymbol;

    function name() public view virtual override returns (string memory) {
        return _contractName;
    }

    function symbol() public view virtual override returns (string memory) {
        return _contractSymbol;
    }

    function _setNameAndSymbol(string memory name_, string memory symbol_) internal {
        _contractName = name_;
        _contractSymbol = symbol_;
    }
}

abstract contract ERC721OpenZeppelin is ERC721OpenZeppelinBase {
    constructor(string memory name_, string memory symbol_) ERC721("", "") {
        _setNameAndSymbol(name_, symbol_);
    }
}

abstract contract ERC721OpenZeppelinInitializable is OwnablePermissions, ERC721OpenZeppelinBase {

    error ERC721OpenZeppelinInitializable__AlreadyInitializedERC721();

    /// @notice Specifies whether or not the contract is initialized
    bool private _erc721Initialized;

    /// @dev Initializes parameters of ERC721 tokens.
    /// These cannot be set in the constructor because this contract is optionally compatible with EIP-1167.
    function initializeERC721(string memory name_, string memory symbol_) public {
        _requireCallerIsContractOwner();

        if(_erc721Initialized) {
            revert ERC721OpenZeppelinInitializable__AlreadyInitializedERC721();
        }

        _erc721Initialized = true;

        _setNameAndSymbol(name_, symbol_);
    }
}

File 11 of 129 : CreatorTokenBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../access/OwnablePermissions.sol";
import "../interfaces/ICreatorToken.sol";
import "../interfaces/ICreatorTokenTransferValidator.sol";
import "../utils/TransferValidation.sol";
import "@openzeppelin/contracts/interfaces/IERC165.sol";

/**
 * @title CreatorTokenBase
 * @author Limit Break, Inc.
 * @notice CreatorTokenBase is an abstract contract that provides basic functionality for managing token 
 * transfer policies through an implementation of ICreatorTokenTransferValidator. This contract is intended to be used
 * as a base for creator-specific token contracts, enabling customizable transfer restrictions and security policies.
 *
 * <h4>Features:</h4>
 * <ul>Ownable: This contract can have an owner who can set and update the transfer validator.</ul>
 * <ul>TransferValidation: Implements the basic token transfer validation interface.</ul>
 * <ul>ICreatorToken: Implements the interface for creator tokens, providing view functions for token security policies.</ul>
 *
 * <h4>Benefits:</h4>
 * <ul>Provides a flexible and modular way to implement custom token transfer restrictions and security policies.</ul>
 * <ul>Allows creators to enforce policies such as whitelisted operators and permitted contract receivers.</ul>
 * <ul>Can be easily integrated into other token contracts as a base contract.</ul>
 *
 * <h4>Intended Usage:</h4>
 * <ul>Use as a base contract for creator token implementations that require advanced transfer restrictions and 
 *   security policies.</ul>
 * <ul>Set and update the ICreatorTokenTransferValidator implementation contract to enforce desired policies for the 
 *   creator token.</ul>
 */
abstract contract CreatorTokenBase is OwnablePermissions, TransferValidation, ICreatorToken {
    
    error CreatorTokenBase__InvalidTransferValidatorContract();
    error CreatorTokenBase__SetTransferValidatorFirst();

    address public constant DEFAULT_TRANSFER_VALIDATOR = address(0x0000721C310194CcfC01E523fc93C9cCcFa2A0Ac);
    TransferSecurityLevels public constant DEFAULT_TRANSFER_SECURITY_LEVEL = TransferSecurityLevels.One;
    uint120 public constant DEFAULT_OPERATOR_WHITELIST_ID = uint120(1);

    ICreatorTokenTransferValidator private transferValidator;

    /**
     * @notice Allows the contract owner to set the transfer validator to the official validator contract
     *         and set the security policy to the recommended default settings.
     * @dev    May be overridden to change the default behavior of an individual collection.
     */
    function setToDefaultSecurityPolicy() public virtual {
        _requireCallerIsContractOwner();
        setTransferValidator(DEFAULT_TRANSFER_VALIDATOR);
        ICreatorTokenTransferValidator(DEFAULT_TRANSFER_VALIDATOR).setTransferSecurityLevelOfCollection(address(this), DEFAULT_TRANSFER_SECURITY_LEVEL);
        ICreatorTokenTransferValidator(DEFAULT_TRANSFER_VALIDATOR).setOperatorWhitelistOfCollection(address(this), DEFAULT_OPERATOR_WHITELIST_ID);
    }

    /**
     * @notice Allows the contract owner to set the transfer validator to a custom validator contract
     *         and set the security policy to their own custom settings.
     */
    function setToCustomValidatorAndSecurityPolicy(
        address validator, 
        TransferSecurityLevels level, 
        uint120 operatorWhitelistId, 
        uint120 permittedContractReceiversAllowlistId) public {
        _requireCallerIsContractOwner();

        setTransferValidator(validator);

        ICreatorTokenTransferValidator(validator).
            setTransferSecurityLevelOfCollection(address(this), level);

        ICreatorTokenTransferValidator(validator).
            setOperatorWhitelistOfCollection(address(this), operatorWhitelistId);

        ICreatorTokenTransferValidator(validator).
            setPermittedContractReceiverAllowlistOfCollection(address(this), permittedContractReceiversAllowlistId);
    }

    /**
     * @notice Allows the contract owner to set the security policy to their own custom settings.
     * @dev    Reverts if the transfer validator has not been set.
     */
    function setToCustomSecurityPolicy(
        TransferSecurityLevels level, 
        uint120 operatorWhitelistId, 
        uint120 permittedContractReceiversAllowlistId) public {
        _requireCallerIsContractOwner();

        ICreatorTokenTransferValidator validator = getTransferValidator();
        if (address(validator) == address(0)) {
            revert CreatorTokenBase__SetTransferValidatorFirst();
        }

        validator.setTransferSecurityLevelOfCollection(address(this), level);
        validator.setOperatorWhitelistOfCollection(address(this), operatorWhitelistId);
        validator.setPermittedContractReceiverAllowlistOfCollection(address(this), permittedContractReceiversAllowlistId);
    }

    /**
     * @notice Sets the transfer validator for the token contract.
     *
     * @dev    Throws when provided validator contract is not the zero address and doesn't support 
     *         the ICreatorTokenTransferValidator interface. 
     * @dev    Throws when the caller is not the contract owner.
     *
     * @dev    <h4>Postconditions:</h4>
     *         1. The transferValidator address is updated.
     *         2. The `TransferValidatorUpdated` event is emitted.
     *
     * @param transferValidator_ The address of the transfer validator contract.
     */
    function setTransferValidator(address transferValidator_) public {
        _requireCallerIsContractOwner();

        bool isValidTransferValidator = false;

        if(transferValidator_.code.length > 0) {
            try IERC165(transferValidator_).supportsInterface(type(ICreatorTokenTransferValidator).interfaceId) 
                returns (bool supportsInterface) {
                isValidTransferValidator = supportsInterface;
            } catch {}
        }

        if(transferValidator_ != address(0) && !isValidTransferValidator) {
            revert CreatorTokenBase__InvalidTransferValidatorContract();
        }

        emit TransferValidatorUpdated(address(transferValidator), transferValidator_);

        transferValidator = ICreatorTokenTransferValidator(transferValidator_);
    }

    /**
     * @notice Returns the transfer validator contract address for this token contract.
     */
    function getTransferValidator() public view override returns (ICreatorTokenTransferValidator) {
        return transferValidator;
    }

    /**
     * @notice Returns the security policy for this token contract, which includes:
     *         Transfer security level, operator whitelist id, permitted contract receiver allowlist id.
     */
    function getSecurityPolicy() public view override returns (CollectionSecurityPolicy memory) {
        if (address(transferValidator) != address(0)) {
            return transferValidator.getCollectionSecurityPolicy(address(this));
        }

        return CollectionSecurityPolicy({
            transferSecurityLevel: TransferSecurityLevels.Zero,
            operatorWhitelistId: 0,
            permittedContractReceiversId: 0
        });
    }

    /**
     * @notice Returns the list of all whitelisted operators for this token contract.
     * @dev    This can be an expensive call and should only be used in view-only functions.
     */
    function getWhitelistedOperators() public view override returns (address[] memory) {
        if (address(transferValidator) != address(0)) {
            return transferValidator.getWhitelistedOperators(
                transferValidator.getCollectionSecurityPolicy(address(this)).operatorWhitelistId);
        }

        return new address[](0);
    }

    /**
     * @notice Returns the list of permitted contract receivers for this token contract.
     * @dev    This can be an expensive call and should only be used in view-only functions.
     */
    function getPermittedContractReceivers() public view override returns (address[] memory) {
        if (address(transferValidator) != address(0)) {
            return transferValidator.getPermittedContractReceivers(
                transferValidator.getCollectionSecurityPolicy(address(this)).permittedContractReceiversId);
        }

        return new address[](0);
    }

    /**
     * @notice Checks if an operator is whitelisted for this token contract.
     * @param operator The address of the operator to check.
     */
    function isOperatorWhitelisted(address operator) public view override returns (bool) {
        if (address(transferValidator) != address(0)) {
            return transferValidator.isOperatorWhitelisted(
                transferValidator.getCollectionSecurityPolicy(address(this)).operatorWhitelistId, operator);
        }

        return false;
    }

    /**
     * @notice Checks if a contract receiver is permitted for this token contract.
     * @param receiver The address of the receiver to check.
     */
    function isContractReceiverPermitted(address receiver) public view override returns (bool) {
        if (address(transferValidator) != address(0)) {
            return transferValidator.isContractReceiverPermitted(
                transferValidator.getCollectionSecurityPolicy(address(this)).permittedContractReceiversId, receiver);
        }

        return false;
    }

    /**
     * @notice Determines if a transfer is allowed based on the token contract's security policy.  Use this function
     *         to simulate whether or not a transfer made by the specified `caller` from the `from` address to the `to`
     *         address would be allowed by this token's security policy.
     *
     * @notice This function only checks the security policy restrictions and does not check whether token ownership
     *         or approvals are in place. 
     *
     * @param caller The address of the simulated caller.
     * @param from   The address of the sender.
     * @param to     The address of the receiver.
     * @return       True if the transfer is allowed, false otherwise.
     */
    function isTransferAllowed(address caller, address from, address to) public view override returns (bool) {
        if (address(transferValidator) != address(0)) {
            try transferValidator.applyCollectionTransferPolicy(caller, from, to) {
                return true;
            } catch {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy.
     *      Inheriting contracts are responsible for overriding the _beforeTokenTransfer function, or its equivalent
     *      and calling _validateBeforeTransfer so that checks can be properly applied during token transfers.
     *
     * @dev Throws when the transfer doesn't comply with the collection's transfer policy, if the transferValidator is
     *      set to a non-zero address.
     *
     * @param caller  The address of the caller.
     * @param from    The address of the sender.
     * @param to      The address of the receiver.
     */
    function _preValidateTransfer(
        address caller, 
        address from, 
        address to, 
        uint256 /*tokenId*/, 
        uint256 /*value*/) internal virtual override {
        if (address(transferValidator) != address(0)) {
            transferValidator.applyCollectionTransferPolicy(caller, from, to);
        }
    }
}

File 12 of 129 : TransferPolicy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

enum AllowlistTypes {
    Operators,
    PermittedContractReceivers
}

enum ReceiverConstraints {
    None,
    NoCode,
    EOA
}

enum CallerConstraints {
    None,
    OperatorWhitelistEnableOTC,
    OperatorWhitelistDisableOTC
}

enum StakerConstraints {
    None,
    CallerIsTxOrigin,
    EOA
}

enum TransferSecurityLevels {
    Zero,
    One,
    Two,
    Three,
    Four,
    Five,
    Six
}

struct TransferSecurityPolicy {
    CallerConstraints callerConstraints;
    ReceiverConstraints receiverConstraints;
}

struct CollectionSecurityPolicy {
    TransferSecurityLevels transferSecurityLevel;
    uint120 operatorWhitelistId;
    uint120 permittedContractReceiversId;
}

File 13 of 129 : TransferValidation.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/Context.sol";

/**
 * @title TransferValidation
 * @author Limit Break, Inc.
 * @notice A mix-in that can be combined with ERC-721 contracts to provide more granular hooks.
 * Openzeppelin's ERC721 contract only provides hooks for before and after transfer.  This allows
 * developers to validate or customize transfers within the context of a mint, a burn, or a transfer.
 */
abstract contract TransferValidation is Context {
    
    error ShouldNotMintToBurnAddress();

    /// @dev Inheriting contracts should call this function in the _beforeTokenTransfer function to get more granular hooks.
    function _validateBeforeTransfer(address from, address to, uint256 tokenId) internal virtual {
        bool fromZeroAddress = from == address(0);
        bool toZeroAddress = to == address(0);

        if(fromZeroAddress && toZeroAddress) {
            revert ShouldNotMintToBurnAddress();
        } else if(fromZeroAddress) {
            _preValidateMint(_msgSender(), to, tokenId, msg.value);
        } else if(toZeroAddress) {
            _preValidateBurn(_msgSender(), from, tokenId, msg.value);
        } else {
            _preValidateTransfer(_msgSender(), from, to, tokenId, msg.value);
        }
    }

    /// @dev Inheriting contracts should call this function in the _afterTokenTransfer function to get more granular hooks.
    function _validateAfterTransfer(address from, address to, uint256 tokenId) internal virtual {
        bool fromZeroAddress = from == address(0);
        bool toZeroAddress = to == address(0);

        if(fromZeroAddress && toZeroAddress) {
            revert ShouldNotMintToBurnAddress();
        } else if(fromZeroAddress) {
            _postValidateMint(_msgSender(), to, tokenId, msg.value);
        } else if(toZeroAddress) {
            _postValidateBurn(_msgSender(), from, tokenId, msg.value);
        } else {
            _postValidateTransfer(_msgSender(), from, to, tokenId, msg.value);
        }
    }

    /// @dev Optional validation hook that fires before a mint
    function _preValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a mint
    function _postValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a burn
    function _preValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a burn
    function _postValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires before a transfer
    function _preValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}

    /// @dev Optional validation hook that fires after a transfer
    function _postValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}
}

File 14 of 129 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 15 of 129 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";

File 16 of 129 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

File 17 of 129 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

File 18 of 129 : ERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/ERC1155.sol)

pragma solidity ^0.8.0;

import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./extensions/IERC1155MetadataURI.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
    using Address for address;

    // Mapping from token ID to account balances
    mapping(uint256 => mapping(address => uint256)) private _balances;

    // Mapping from account to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
    string private _uri;

    /**
     * @dev See {_setURI}.
     */
    constructor(string memory uri_) {
        _setURI(uri_);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC1155MetadataURI-uri}.
     *
     * This implementation returns the same URI for *all* token types. It relies
     * on the token type ID substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * Clients calling this function must replace the `\{id\}` substring with the
     * actual token type ID.
     */
    function uri(uint256) public view virtual override returns (string memory) {
        return _uri;
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
        require(account != address(0), "ERC1155: address zero is not a valid owner");
        return _balances[id][account];
    }

    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(
        address[] memory accounts,
        uint256[] memory ids
    ) public view virtual override returns (uint256[] memory) {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }

    /**
     * @dev Sets a new URI for all token types, by relying on the token type ID
     * substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * By this mechanism, any occurrence of the `\{id\}` substring in either the
     * URI or any of the amounts in the JSON file at said URI will be replaced by
     * clients with the token type ID.
     *
     * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
     * interpreted by clients as
     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
     * for token type ID 0x4cce0.
     *
     * See {uri}.
     *
     * Because these URIs cannot be meaningfully represented by the {URI} event,
     * this function emits no events.
     */
    function _setURI(string memory newuri) internal virtual {
        _uri = newuri;
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `from`
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `from` must have at least `amount` tokens of token type `id`.
     */
    function _burn(address from, uint256 id, uint256 amount) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        require(owner != operator, "ERC1155: setting approval status for self");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `ids` and `amounts` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }
}

File 19 of 129 : IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)

pragma solidity ^0.8.0;

import "../IERC1155.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

File 20 of 129 : IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(
        address[] calldata accounts,
        uint256[] calldata ids
    ) external view returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

File 21 of 129 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

File 22 of 129 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

File 23 of 129 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 24 of 129 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 25 of 129 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 26 of 129 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 27 of 129 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner or approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownerOf(tokenId) != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId, 1);

        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
        require(!_exists(tokenId), "ERC721: token already minted");

        unchecked {
            // Will not overflow unless all 2**256 token ids are minted to the same owner.
            // Given that tokens are minted one by one, it is impossible in practice that
            // this ever happens. Might change if we allow batch minting.
            // The ERC fails to describe this case.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId, 1);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId, 1);

        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
        owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        delete _tokenApprovals[tokenId];

        unchecked {
            // Cannot overflow, as that would require more tokens to be burned/transferred
            // out than the owner initially received through minting and transferring in.
            _balances[owner] -= 1;
        }
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId, 1);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId, 1);

        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];

        unchecked {
            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
            // `from`'s balance is the number of token held, which is at least one before the current
            // transfer.
            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
            // all 2**256 token ids to be minted, which in practice is impossible.
            _balances[from] -= 1;
            _balances[to] += 1;
        }
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId, 1);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
     * - When `from` is zero, the tokens will be minted for `to`.
     * - When `to` is zero, ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
     * - When `from` is zero, the tokens were minted for `to`.
     * - When `to` is zero, ``from``'s tokens were burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
     * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
     * that `ownerOf(tokenId)` is `a`.
     */
    // solhint-disable-next-line func-name-mixedcase
    function __unsafe_increaseBalance(address account, uint256 amount) internal {
        _balances[account] += amount;
    }
}

File 28 of 129 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 29 of 129 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 30 of 129 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 31 of 129 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 32 of 129 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 33 of 129 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 34 of 129 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 35 of 129 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 36 of 129 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 37 of 129 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 38 of 129 : IAllowanceTransfer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface IAllowanceTransfer {
  struct PermitDetails {
    address token;
    uint160 amount;
    uint48 expiration;
    uint48 nonce;
  }

  struct PermitBatch {
    PermitDetails[] details;
    address spender;
    uint256 sigDeadline;
  }

  struct AllowanceTransferDetails {
    address from;
    address to;
    uint160 amount;
    address token;
  }

  function permit(
    address owner,
    PermitBatch memory permitBatch,
    bytes calldata signature
  ) external;

  function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
}

File 39 of 129 : IBlur.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IBlur {
  enum Side {
    Buy,
    Sell
  }

  enum AssetType {
    ERC721,
    ERC1155
  }

  enum SignatureVersion {
    Single,
    Bulk
  }

  struct Fee {
    uint16 rate;
    address payable recipient;
  }

  struct Order {
    address trader;
    Side side;
    address matchingPolicy;
    IERC165 collection;
    uint256 tokenId;
    uint256 amount;
    address paymentToken;
    uint256 price;
    uint256 listingTime;
    uint256 expirationTime;
    Fee[] fees;
    uint256 salt;
    bytes extraParams;
  }

  struct Input {
    Order order;
    uint8 v;
    bytes32 r;
    bytes32 s;
    bytes extraSignature;
    SignatureVersion signatureVersion;
    uint256 blockNumber;
  }

  function execute(Input calldata sell, Input calldata buy) external payable;
}

File 40 of 129 : ICaviarV1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface ICaviarPoolV1 {
  struct Message {
    bytes32 id;
    bytes payload;
    uint256 timestamp;
    bytes signature;
  }

  function nft() external returns (address);

  function baseToken() external returns (address);

  function buyQuote(uint256 outputAmount) external view returns (uint256);

  function nftBuy(
    uint256[] calldata tokenIds,
    uint256 maxInputAmount,
    uint256 deadline
  ) external payable returns (uint256 inputAmount);

  function nftSell(
    uint256[] calldata tokenIds,
    uint256 minOutputAmount,
    uint256 deadline,
    bytes32[][] calldata proofs,
    Message[] calldata messages
  ) external returns (uint256 outputAmount);
}

File 41 of 129 : ICollectionXyz.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface ICollectionPool {
  enum PoolType {
    TOKEN,
    NFT,
    TRADE
  }

  enum PoolVariant {
    ENUMERABLE_ETH,
    MISSING_ENUMERABLE_ETH,
    ENUMERABLE_ERC20,
    MISSING_ENUMERABLE_ERC20
  }

  struct CurveParams {
    uint128 spotPrice;
    uint128 delta;
    bytes props;
    bytes state;
  }

  struct CurveFees {
    uint256 trade;
    uint256 protocol;
    uint256[] royalties;
  }

  function nft() external returns (IERC721);

  function token() external returns (IERC20);

  function poolType() external view returns (PoolType);

  function poolVariant() external pure returns (PoolVariant);

  function externalFilter() external view returns (address);

  function getBuyNFTQuote(
    uint256 numNFTs
  )
    external
    view
    returns (
      CurveParams memory newParams,
      uint256 totalAmount,
      uint256 inputAmount,
      CurveFees memory fees
    );
}

interface ICollectionRouter {
  struct PoolSwapSpecific {
    ICollectionPool pool;
    uint256[] nftIds;
    bytes32[] proof;
    bool[] proofFlags;
    /// @dev only used for selling into pools
    bytes externalFilterContext;
  }

  function swapETHForSpecificNFTs(
    PoolSwapSpecific[] calldata swapList,
    address ethRecipient,
    address nftRecipient,
    uint256 deadline
  ) external payable returns (uint256 remainingValue);

  function swapERC20ForSpecificNFTs(
    PoolSwapSpecific[] calldata swapList,
    uint256 inputAmount,
    address nftRecipient,
    uint256 deadline
  ) external returns (uint256 remainingValue);

  function swapNFTsForToken(
    PoolSwapSpecific[] calldata swapList,
    uint256 minOutput,
    address tokenRecipient,
    uint256 deadline
  ) external returns (uint256 outputAmount);
}

File 42 of 129 : IConduit.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface IConduitController {
  function getConduitCodeHashes()
    external
    view
    returns (bytes32 creationCodeHash, bytes32 runtimeCodeHash);
}

interface IConduit {
  enum ConduitItemType {
    NATIVE, // Unused
    ERC20,
    ERC721,
    ERC1155
  }

  struct ConduitTransfer {
    ConduitItemType itemType;
    address token;
    address from;
    address to;
    uint256 identifier;
    uint256 amount;
  }

  function execute(ConduitTransfer[] calldata transfers) external returns (bytes4 magicValue);
}

File 43 of 129 : ICryptoPunksMarket.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface ICryptoPunksMarket {
  struct BuyOrder {
    address buyer;
    uint256 price;
    uint256 punkIndex;
  }

  function punkIndexToAddress(uint256 punkIndex) external view returns (address owner);

  function balanceOf(address owner) external view returns (uint256 balance);

  function transferPunk(address to, uint256 punkIndex) external;

  function buyPunk(uint256 punkIndex) external payable;

  function offerPunkForSaleToAddress(
    uint256 punkIndex,
    uint256 minSalePriceInWei,
    address to
  ) external;
}

File 44 of 129 : IDittoPool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IDittoPool {
  struct SwapTokensForNftsArgs {
    uint256[] nftIds;
    uint256 maxExpectedTokenInput;
    address tokenSender;
    address nftRecipient;
    bytes swapData;
  }

  struct SwapNftsForTokensArgs {
    uint256[] nftIds;
    uint256[] lpIds;
    uint256 minExpectedTokenOutput;
    address nftSender;
    address tokenRecipient;
    bytes permitterData;
    bytes swapData;
  }

  function token() external returns (IERC20);

  function swapNftsForTokens(
    SwapNftsForTokensArgs calldata args_
  ) external returns (uint256 outputAmount);

  function swapTokensForNfts(
    SwapTokensForNftsArgs calldata args_
  ) external returns (uint256 inputAmount);
}

File 45 of 129 : IEIP2612.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface IEIP2612 {
  function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  function nonces(address owner) external view returns (uint256);

  function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 46 of 129 : IElement.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IElement {
  struct Signature {
    uint8 signatureType;
    uint8 v;
    bytes32 r;
    bytes32 s;
  }

  struct Property {
    address propertyValidator;
    bytes propertyData;
  }

  struct Fee {
    address recipient;
    uint256 amount;
    bytes feeData;
  }

  struct NFTSellOrder {
    address maker;
    address taker;
    uint256 expiry;
    uint256 nonce;
    IERC20 erc20Token;
    uint256 erc20TokenAmount;
    Fee[] fees;
    address nft;
    uint256 nftId;
  }

  struct NFTBuyOrder {
    address maker;
    address taker;
    uint256 expiry;
    uint256 nonce;
    IERC20 erc20Token;
    uint256 erc20TokenAmount;
    Fee[] fees;
    address nft;
    uint256 nftId;
    Property[] nftProperties;
  }

  struct ERC1155SellOrder {
    address maker;
    address taker;
    uint256 expiry;
    uint256 nonce;
    IERC20 erc20Token;
    uint256 erc20TokenAmount;
    Fee[] fees;
    address erc1155Token;
    uint256 erc1155TokenId;
    // End of fields shared with NFTOrder
    uint128 erc1155TokenAmount;
  }

  struct ERC1155BuyOrder {
    address maker;
    address taker;
    uint256 expiry;
    uint256 nonce;
    IERC20 erc20Token;
    uint256 erc20TokenAmount;
    Fee[] fees;
    address erc1155Token;
    uint256 erc1155TokenId;
    Property[] erc1155TokenProperties;
    // End of fields shared with NFTOrder
    uint128 erc1155TokenAmount;
  }

  struct BatchSignedOrder {
    address maker;
    uint256 listingTime;
    uint256 expirationTime;
    uint256 startNonce;
    address erc20Token;
    address platformFeeRecipient;
    uint8 v;
    bytes32 r;
    bytes32 s;
    bytes collectionsBytes;
  }

  /// @param data1 [56 bits(startNonce) + 8 bits(v) + 32 bits(listingTime) + 160 bits(maker)]
  /// @param data2 [64 bits(taker part1) + 32 bits(expiryTime) + 160 bits(erc20Token)]
  /// @param data3 [96 bits(taker part2) + 160 bits(platformFeeRecipient)]
  struct Parameter {
    uint256 data1;
    uint256 data2;
    uint256 data3;
    bytes32 r;
    bytes32 s;
  }

  /// @param data1 [56 bits(startNonce) + 8 bits(v) + 32 bits(listingTime) + 160 bits(maker)]
  /// @param data2 [64 bits(taker part1) + 32 bits(expiryTime) + 160 bits(erc20Token)]
  /// @param data3 [96 bits(taker part2) + 160 bits(platformFeeRecipient)]
  struct Parameters {
    uint256 data1;
    uint256 data2;
    uint256 data3;
    bytes32 r;
    bytes32 s;
    bytes collections;
  }

  function buyERC721Ex(
    NFTSellOrder calldata sellOrder,
    Signature calldata signature,
    address taker,
    bytes calldata takerData
  ) external payable;

  function batchBuyERC721sEx(
    NFTSellOrder[] calldata sellOrders,
    Signature[] calldata signatures,
    address[] calldata takers,
    bytes[] calldata takerDatas,
    bool revertIfIncomplete
  ) external payable returns (bool[] memory successes);

  function buyERC1155Ex(
    ERC1155SellOrder calldata sellOrder,
    Signature calldata signature,
    address taker,
    uint128 erc1155BuyAmount,
    bytes calldata takerData
  ) external payable;

  function batchBuyERC1155sEx(
    ERC1155SellOrder[] calldata sellOrders,
    Signature[] calldata signatures,
    address[] calldata takers,
    uint128[] calldata erc1155TokenAmounts,
    bytes[] calldata takerDatas,
    bool revertIfIncomplete
  ) external payable returns (bool[] memory successes);

  function sellERC721(
    NFTBuyOrder calldata buyOrder,
    Signature calldata signature,
    uint256 erc721TokenId,
    bool unwrapNativeToken,
    bytes calldata takerData
  ) external;

  function sellERC1155(
    ERC1155BuyOrder calldata buyOrder,
    Signature calldata signature,
    uint256 erc1155TokenId,
    uint128 erc1155SellAmount,
    bool unwrapNativeToken,
    bytes calldata takerData
  ) external;

  function fillBatchSignedERC721Order(Parameter calldata parameter, bytes calldata collections)
    external
    payable;

  /// @param additional1 [96 bits(withdrawETHAmount) + 160 bits(erc20Token)]
  /// @param additional2 [8 bits(revertIfIncomplete) + 88 bits(unused) + 160 bits(royaltyFeeRecipient)]
  function fillBatchSignedERC721Orders(
    Parameters[] calldata parameters,
    uint256 additional1,
    uint256 additional2
  ) external payable;
}

File 47 of 129 : IExchangeKind.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

enum ExchangeKind {
  WYVERN_V23,
  LOOKS_RARE,
  ZEROEX_V4,
  FOUNDATION,
  X2Y2,
  SEAPORT
}

File 48 of 129 : IFoundation.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface IFoundation {
  function buyV2(
    IERC721 nftContract,
    uint256 tokenId,
    uint256 maxPrice,
    address referrer
  ) external payable;
}

File 49 of 129 : ILooksRare.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface ILooksRare {
  struct MakerOrder {
    bool isOrderAsk;
    address signer;
    IERC165 collection;
    uint256 price;
    uint256 tokenId;
    uint256 amount;
    address strategy;
    IERC20 currency;
    uint256 nonce;
    uint256 startTime;
    uint256 endTime;
    uint256 minPercentageToAsk;
    bytes params;
    uint8 v;
    bytes32 r;
    bytes32 s;
  }

  struct TakerOrder {
    bool isOrderAsk;
    address taker;
    uint256 price;
    uint256 tokenId;
    uint256 minPercentageToAsk;
    bytes params;
  }

  function transferSelectorNFT() external view returns (ILooksRareTransferSelectorNFT);

  function matchAskWithTakerBidUsingETHAndWETH(
    TakerOrder calldata takerBid,
    MakerOrder calldata makerAsk
  ) external payable;

  function matchAskWithTakerBid(
    TakerOrder calldata takerBid,
    MakerOrder calldata makerAsk
  ) external payable;

  function matchBidWithTakerAsk(
    TakerOrder calldata takerAsk,
    MakerOrder calldata makerBid
  ) external;
}

interface ILooksRareTransferSelectorNFT {
  function TRANSFER_MANAGER_ERC721() external view returns (address);

  function TRANSFER_MANAGER_ERC1155() external view returns (address);
}

File 50 of 129 : ILooksRareV2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface ILooksRareV2 {
  enum QuoteType {
    Bid,
    Ask
  }

  enum CollectionType {
    ERC721,
    ERC1155
  }

  enum MerkleTreeNodePosition {
    Left,
    Right
  }

  struct MakerOrder {
    QuoteType quoteType;
    uint256 globalNonce;
    uint256 subsetNonce;
    uint256 orderNonce;
    uint256 strategyId;
    CollectionType collectionType;
    address collection;
    IERC20 currency;
    address signer;
    uint256 startTime;
    uint256 endTime;
    uint256 price;
    uint256[] itemIds;
    uint256[] amounts;
    bytes additionalParameters;
  }

  struct TakerOrder {
    address recipient;
    bytes additionalParameters;
  }

  struct MerkleTreeNode {
    bytes32 value;
    MerkleTreeNodePosition position;
  }

  struct MerkleTree {
    bytes32 root;
    MerkleTreeNode[] proof;
  }

  function transferManager() external view returns (ITransferManager);

  function executeTakerAsk(
    TakerOrder calldata takerAsk,
    MakerOrder calldata makerBid,
    bytes calldata makerSignature,
    MerkleTree calldata merkleTree,
    address affiliate
  ) external;

  function executeTakerBid(
    TakerOrder calldata takerBid,
    MakerOrder calldata makerAsk,
    bytes calldata makerSignature,
    MerkleTree calldata merkleTree,
    address affiliate
  ) external payable;
}

interface ITransferManager {
  function grantApprovals(address[] calldata operators) external;
}

File 51 of 129 : IMidaswap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface IMidasRouter {
  function getMinAmountIn(
    address pair,
    uint256[] calldata tokenIds
  ) external view returns (uint128 totalAmount);
}

interface IMidasPair {
  function sellNFT(uint256 nftId, address to) external returns (uint128 amountOut);

  function buyNFT(uint256 nftId, address to) external;
}

interface IMidasFactory {
  function getPairERC721(address tokenA, address tokenB) external view returns (address pair);
}

File 52 of 129 : INFTX.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {INFTXVaultFactory} from "./INFTXVaultFactory.sol";

interface INFTXMarketplaceZap {
  function nftxFactory() external view returns (INFTXVaultFactory);

  struct BuyOrder {
    uint256 vaultId;
    address collection;
    uint256[] specificIds;
    uint256 amount;
    address[] path;
    uint256 price;
  }

  struct SellOrder {
    uint256 vaultId;
    address collection;
    IERC20 currency;
    uint256[] specificIds;
    // ERC1155
    uint256[] amounts;
    uint256 price;
    address[] path;
  }

  function mintAndSell721(
    uint256 vaultId,
    uint256[] calldata ids,
    uint256 minEthOut,
    address[] calldata path,
    address to
  ) external;

  function mintAndSell721WETH(
    uint256 vaultId,
    uint256[] calldata ids,
    uint256 minEthOut,
    address[] calldata path,
    address to
  ) external;

  function mintAndSell1155(
    uint256 vaultId,
    uint256[] calldata ids,
    uint256[] calldata amounts,
    uint256 minWethOut,
    address[] calldata path,
    address to
  ) external;

  function mintAndSell1155WETH(
    uint256 vaultId,
    uint256[] calldata ids,
    uint256[] calldata amounts,
    uint256 minWethOut,
    address[] calldata path,
    address to
  ) external;

  function buyAndRedeem(
    uint256 vaultId,
    uint256 amount,
    uint256[] calldata specificIds,
    address[] calldata path,
    address to
  ) external payable;
}

File 53 of 129 : INFTXMarketplace0xZap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {INFTXVaultFactory} from "./INFTXVaultFactory.sol";

interface INFTXMarketplace0xZap {
  function nftxFactory() external view returns (INFTXVaultFactory);

  struct BuyOrder {
    uint256 vaultId;
    address collection;
    uint256[] specificIds;
    uint256 amount;
    uint256 price;
    bytes swapCallData;
  }

  struct SellOrder {
    uint256 vaultId;
    address collection;
    IERC20 currency;
    uint256[] specificIds;
    // For ERC1155 only
    uint256[] amounts;
    uint256 price;
    bytes swapCallData;
  }

  function mintAndSell721(
    uint256 vaultId,
    uint256[] calldata ids,
    bytes calldata swapCallData,
    address payable to
  ) external;

  function mintAndSell1155(
    uint256 vaultId,
    uint256[] calldata ids,
    uint256[] calldata amounts,
    bytes calldata swapCallData,
    address payable to
  ) external;

  function buyAndRedeem(
    uint256 vaultId,
    uint256 amount,
    uint256[] calldata specificIds,
    bytes calldata swapCallData,
    address payable to
  ) external payable;
}

File 54 of 129 : INFTXV3MarketplaceZap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {INFTXVaultFactory} from "./INFTXVaultFactory.sol";

interface INFTXV3MarketplaceZap {
  function nftxVaultFactory() external view returns (INFTXVaultFactory);

  struct SellOrder {
    uint256 vaultId;
    address collection;
    IERC20 currency;
    uint256[] idsIn;
    // For ERC1155 only
    uint256[] amounts;
    uint256 price;
    bytes executeCallData;
    bool deductRoyalty;
  }

  struct BuyOrder {
    uint256 vaultId;
    address collection;
    uint256[] idsOut;
    uint256 price;
    bytes executeCallData;
    uint256 vTokenPremiumLimit;
    bool deductRoyalty;
  }

  function sell721(
    uint256 vaultId,
    uint256[] calldata idsIn,
    bytes calldata executeCallData,
    address payable to,
    bool deductRoyalty
  ) external;

  function sell1155(
    uint256 vaultId,
    uint256[] calldata idsIn,
    uint256[] calldata amounts,
    bytes calldata executeCallData,
    address payable to,
    bool deductRoyalty
  ) external;

  function buyNFTsWithETH(
    uint256 vaultId,
    uint256[] calldata idsOut,
    bytes calldata executeCallData,
    address payable to,
    uint256 vTokenPremiumLimit,
    bool deductRoyalty
  ) external payable;
}

File 55 of 129 : INFTXVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface INFTXVault {
  function is1155() external view returns (bool);
}

File 56 of 129 : INFTXVaultFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface INFTXVaultFactory {
  function vault(uint256 vaultId) external view returns (address);
}

File 57 of 129 : IPaymentProcessor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IPaymentProcessor {
  enum TokenProtocols {
    ERC721,
    ERC1155
  }

  struct SignatureECDSA {
    uint8 v;
    bytes32 r;
    bytes32 s;
  }

  struct MatchedOrder {
    bool sellerAcceptedOffer;
    bool collectionLevelOffer;
    TokenProtocols protocol;
    address paymentCoin;
    address tokenAddress;
    address seller;
    address privateBuyer;
    address buyer;
    address delegatedPurchaser;
    address marketplace;
    uint256 marketplaceFeeNumerator;
    uint256 maxRoyaltyFeeNumerator;
    uint256 listingNonce;
    uint256 offerNonce;
    uint256 listingMinPrice;
    uint256 offerPrice;
    uint256 listingExpiration;
    uint256 offerExpiration;
    uint256 tokenId;
    uint256 amount;
  }

  function buySingleListing(
    MatchedOrder memory saleDetails,
    SignatureECDSA memory signedListing,
    SignatureECDSA memory signedOffer
  ) external payable;
}

File 58 of 129 : IRarible.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IRarible {
  struct AssetType {
    bytes4 assetClass;
    bytes data;
  }

  struct Asset {
    AssetType assetType;
    uint256 value;
  }

  struct Order {
    address maker;
    Asset makeAsset;
    address taker;
    Asset takeAsset;
    uint256 salt;
    uint256 start;
    uint256 end;
    bytes4 dataType;
    bytes data;
  }

  struct NftData {
    uint256 tokenId;
    IERC165 collection;
  }

  function matchOrders(
    Order calldata orderLeft,
    bytes calldata signatureLeft,
    Order calldata orderRight,
    bytes calldata signatureRight
  ) external payable;
}

interface IRaribleTransferManager {
  function TRANSFER_MANAGER() external view returns (address);
}

File 59 of 129 : IReservoirV6_0_1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface IReservoirV6_0_1 {
  struct ExecutionInfo {
    address module;
    bytes data;
    uint256 value;
  }

  function execute(ExecutionInfo[] calldata executionInfos) external payable;
}

File 60 of 129 : ISeaport.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface ISeaport {
  enum OrderType {
    FULL_OPEN,
    PARTIAL_OPEN,
    FULL_RESTRICTED,
    PARTIAL_RESTRICTED
  }

  enum ItemType {
    NATIVE,
    ERC20,
    ERC721,
    ERC1155,
    ERC721_WITH_CRITERIA,
    ERC1155_WITH_CRITERIA
  }

  enum Side {
    OFFER,
    CONSIDERATION
  }

  struct OfferItem {
    ItemType itemType;
    address token;
    uint256 identifierOrCriteria;
    uint256 startAmount;
    uint256 endAmount;
  }

  struct ConsiderationItem {
    ItemType itemType;
    address token;
    uint256 identifierOrCriteria;
    uint256 startAmount;
    uint256 endAmount;
    address recipient;
  }

  struct SpentItem {
    ItemType itemType;
    address token;
    uint256 identifier;
    uint256 amount;
  }

  struct ReceivedItem {
    ItemType itemType;
    address token;
    uint256 identifier;
    uint256 amount;
    address recipient;
  }

  struct OrderComponents {
    address offerer;
    address zone;
    OfferItem[] offer;
    ConsiderationItem[] consideration;
    OrderType orderType;
    uint256 startTime;
    uint256 endTime;
    bytes32 zoneHash;
    uint256 salt;
    bytes32 conduitKey;
    uint256 counter;
  }

  struct OrderParameters {
    address offerer;
    address zone;
    OfferItem[] offer;
    ConsiderationItem[] consideration;
    OrderType orderType;
    uint256 startTime;
    uint256 endTime;
    bytes32 zoneHash;
    uint256 salt;
    bytes32 conduitKey;
    uint256 totalOriginalConsiderationItems;
  }

  struct Order {
    OrderParameters parameters;
    bytes signature;
  }

  struct AdvancedOrder {
    OrderParameters parameters;
    uint120 numerator;
    uint120 denominator;
    bytes signature;
    bytes extraData;
  }

  struct CriteriaResolver {
    uint256 orderIndex;
    Side side;
    uint256 index;
    uint256 identifier;
    bytes32[] criteriaProof;
  }

  struct FulfillmentComponent {
    uint256 orderIndex;
    uint256 itemIndex;
  }

  struct Fulfillment {
    FulfillmentComponent[] offerComponents;
    FulfillmentComponent[] considerationComponents;
  }

  struct Execution {
    ReceivedItem item;
    address offerer;
    bytes32 conduitKey;
  }

  struct ZoneParameters {
    bytes32 orderHash;
    address fulfiller;
    address offerer;
    SpentItem[] offer;
    ReceivedItem[] consideration;
    bytes extraData;
    bytes32[] orderHashes;
    uint256 startTime;
    uint256 endTime;
    bytes32 zoneHash;
  }

  struct Schema {
    uint256 id;
    bytes metadata;
  }

  function getOrderHash(OrderComponents calldata order) external view returns (bytes32 orderHash);

  function getOrderStatus(bytes32 orderHash)
    external
    view
    returns (
      bool isValidated,
      bool isCancelled,
      uint256 totalFilled,
      uint256 totalSize
    );

  function getCounter(address offerer) external view returns (uint256 counter);

  function fulfillAdvancedOrder(
    AdvancedOrder calldata advancedOrder,
    CriteriaResolver[] calldata criteriaResolvers,
    bytes32 fulfillerConduitKey,
    address recipient
  ) external payable returns (bool fulfilled);

  function fulfillAvailableAdvancedOrders(
    AdvancedOrder[] memory advancedOrders,
    CriteriaResolver[] calldata criteriaResolvers,
    FulfillmentComponent[][] calldata offerFulfillments,
    FulfillmentComponent[][] calldata considerationFulfillments,
    bytes32 fulfillerConduitKey,
    address recipient,
    uint256 maximumFulfilled
  ) external payable returns (bool[] memory availableOrders, Execution[] memory executions);

  function matchOrders(Order[] calldata orders, Fulfillment[] calldata fulfillments)
    external
    payable
    returns (Execution[] memory executions);

  function matchAdvancedOrders(
    AdvancedOrder[] calldata advancedOrders,
    CriteriaResolver[] calldata criteriaResolvers,
    Fulfillment[] calldata fulfillments,
    address recipient
  ) external payable returns (Execution[] memory executions);
}

File 61 of 129 : ISudoswap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface ISudoswapPair {
  enum PairVariant {
    ENUMERABLE_ETH,
    MISSING_ENUMERABLE_ETH,
    ENUMERABLE_ERC20,
    MISSING_ENUMERABLE_ERC20
  }

  function nft() external returns (IERC721);

  function token() external returns (IERC20);

  function pairVariant() external pure returns (PairVariant);

  function getBuyNFTQuote(uint256 numNFTs)
    external
    view
    returns (
      uint8 error,
      uint256 newSpotPrice,
      uint256 newDelta,
      uint256 inputAmount,
      uint256 protocolFee
    );
}

interface ISudoswapRouter {
  struct PairSwapSpecific {
    ISudoswapPair pair;
    uint256[] nftIds;
  }

  function swapETHForSpecificNFTs(
    PairSwapSpecific[] calldata swapList,
    address ethRecipient,
    address nftRecipient,
    uint256 deadline
  ) external payable returns (uint256 remainingValue);

  function swapERC20ForSpecificNFTs(
    PairSwapSpecific[] calldata swapList,
    uint256 inputAmount,
    address nftRecipient,
    uint256 deadline
  ) external returns (uint256 remainingValue);

  function swapNFTsForToken(
    PairSwapSpecific[] calldata swapList,
    uint256 minOutput,
    address tokenRecipient,
    uint256 deadline
  ) external returns (uint256 outputAmount);
}

File 62 of 129 : ISudoswapV2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface ISudoswapPairV2 {
  enum PairVariant {
    ERC721_ETH,
    ERC721_ERC20,
    ERC1155_ETH,
    ERC1155_ERC20
  }

  function nft() external returns (address);

  function nftId() external returns (uint256 id);

  function token() external returns (IERC20);

  function pairVariant() external pure returns (PairVariant);

  function getBuyNFTQuote(
    uint256 assetId,
    uint256 numNFTs
  )
    external
    view
    returns (
      uint8 error,
      uint256 newSpotPrice,
      uint256 newDelta,
      uint256 inputAmount,
      uint256 protocolFee,
      uint256 royaltyAmount
    );

  function swapTokenForSpecificNFTs(
    uint256[] calldata nftIds,
    uint256 maxExpectedTokenInput,
    address nftRecipient,
    bool isRouter,
    address routerCaller
  ) external payable returns (uint256);

  function swapNFTsForToken(
    uint256[] calldata nftIds,
    uint256 minExpectedTokenOutput,
    address payable tokenRecipient,
    bool isRouter,
    address routerCaller
  ) external returns (uint256 outputAmount);
}

File 63 of 129 : ISuperRare.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface ISuperRare {
  function buy(
    IERC721 nftContract,
    uint256 tokenId,
    address currency,
    uint256 price
  ) external payable;
}

File 64 of 129 : IUniswapV3Router.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IUniswapV3Router {
  struct ExactOutputSingleParams {
    IERC20 tokenIn;
    IERC20 tokenOut;
    uint24 fee;
    address recipient;
    uint256 amountOut;
    uint256 amountInMaximum;
    uint160 sqrtPriceLimitX96;
  }

  function exactOutputSingle(ExactOutputSingleParams calldata params)
    external
    payable
    returns (uint256 amountIn);

  
  struct ExactInputSingleParams {
    IERC20 tokenIn;
    IERC20 tokenOut;
    uint24 fee;
    address recipient;
    uint256 amountIn;
    uint256 amountOutMinimum;
    uint160 sqrtPriceLimitX96;
  }

  function exactInputSingle(ExactInputSingleParams calldata params) 
    external
    payable
    returns (uint256 amountOut);

  function refundETH() external;
}

File 65 of 129 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {
  function deposit() external payable;

  function withdraw(uint256 amount) external;
}

File 66 of 129 : IWyvernV23.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface IWyvernV23 {
  function registry() external view returns (address);

  function tokenTransferProxy() external view returns (address);

  function atomicMatch_(
    address[14] calldata addrs,
    uint256[18] calldata uints,
    uint8[8] calldata feeMethodsSidesKindsHowToCalls,
    bytes calldata calldataBuy,
    bytes calldata calldataSell,
    bytes calldata replacementPatternBuy,
    bytes calldata replacementPatternSell,
    bytes calldata staticExtradataBuy,
    bytes calldata staticExtradataSell,
    uint8[2] calldata vs,
    bytes32[5] calldata rssMetadata
  ) external payable;
}

interface IWyvernV23ProxyRegistry {
  function registerProxy() external;

  function proxies(address user) external view returns (address);
}

File 67 of 129 : IX2Y2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

interface IX2Y2 {
  struct OrderItem {
    uint256 price;
    bytes data;
  }

  struct ERC721Pair {
    IERC721 token;
    uint256 tokenId;
  }

  struct ERC1155Pair {
    IERC1155 token;
    uint256 tokenId;
    uint256 amount;
  }

  struct Order {
    uint256 salt;
    address user;
    uint256 network;
    uint256 intent;
    uint256 delegateType;
    uint256 deadline;
    IERC20 currency;
    bytes dataMask;
    OrderItem[] items;
    bytes32 r;
    bytes32 s;
    uint8 v;
    uint8 signVersion;
  }

  struct SettleShared {
    uint256 salt;
    uint256 deadline;
    uint256 amountToEth;
    uint256 amountToWeth;
    address user;
    bool canFail;
  }

  struct Fee {
    uint256 percentage;
    address to;
  }

  enum Op {
    INVALID,
    COMPLETE_SELL_OFFER,
    COMPLETE_BUY_OFFER,
    CANCEL_OFFER,
    BID,
    COMPLETE_AUCTION,
    REFUND_AUCTION,
    REFUND_AUCTION_STUCK_ITEM
  }

  struct SettleDetail {
    Op op;
    uint256 orderIdx;
    uint256 itemIdx;
    uint256 price;
    bytes32 itemHash;
    address executionDelegate;
    bytes dataReplacement;
    uint256 bidIncentivePct;
    uint256 aucMinIncrementPct;
    uint256 aucIncDurationSecs;
    Fee[] fees;
  }

  struct RunInput {
    Order[] orders;
    SettleDetail[] details;
    SettleShared shared;
    bytes32 r;
    bytes32 s;
    uint8 v;
  }

  function run(RunInput calldata input) external payable;
}

File 68 of 129 : IZeroExV4.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface IZeroExV4 {
  struct Property {
    address propertyValidator;
    bytes propertyData;
  }

  struct Fee {
    address recipient;
    uint256 amount;
    bytes feeData;
  }

  struct ERC721Order {
    uint8 direction;
    address maker;
    address taker;
    uint256 expiry;
    uint256 nonce;
    IERC20 erc20Token;
    uint256 erc20TokenAmount;
    Fee[] fees;
    IERC721 erc721Token;
    uint256 erc721TokenId;
    Property[] erc721TokenProperties;
  }

  struct ERC1155Order {
    uint8 direction;
    address maker;
    address taker;
    uint256 expiry;
    uint256 nonce;
    IERC20 erc20Token;
    uint256 erc20TokenAmount;
    Fee[] fees;
    IERC1155 erc1155Token;
    uint256 erc1155TokenId;
    Property[] erc1155TokenProperties;
    uint128 erc1155TokenAmount;
  }

  struct Signature {
    uint8 signatureType;
    uint8 v;
    bytes32 r;
    bytes32 s;
  }

  function buyERC721(
    ERC721Order calldata sellOrder,
    Signature calldata signature,
    bytes memory callbackData
  ) external payable;

  function batchBuyERC721s(
    ERC721Order[] calldata sellOrders,
    Signature[] calldata signatures,
    bytes[] calldata callbackData,
    bool revertIfIncomplete
  ) external payable returns (bool[] memory);

  function sellERC721(
    ERC721Order calldata buyOrder,
    Signature calldata signature,
    uint256 erc721TokenId,
    bool unwrapNativeToken,
    bytes memory callbackData
  ) external;

  function buyERC1155(
    ERC1155Order calldata sellOrder,
    Signature calldata signature,
    uint128 erc1155BuyAmount,
    bytes calldata callbackData
  ) external payable;

  function batchBuyERC1155s(
    ERC1155Order[] calldata sellOrders,
    Signature[] calldata signatures,
    uint128[] calldata erc1155FillAmounts,
    bytes[] calldata callbackData,
    bool revertIfIncomplete
  ) external payable returns (bool[] memory successes);

  function sellERC1155(
    ERC1155Order calldata buyOrder,
    Signature calldata signature,
    uint256 erc1155TokenId,
    uint128 erc1155SellAmount,
    bool unwrapNativeToken,
    bytes calldata callbackData
  ) external;
}

File 69 of 129 : IZora.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface IZora {
  function fillAsk(
    address tokenContract,
    uint256 tokenId,
    address fillCurrency,
    uint256 fillAmount,
    address finder
  ) external payable;
}

File 70 of 129 : Create3Factory.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.13;

import {CREATE3} from "solmate/src/utils/CREATE3.sol";

contract Create3Factory {
  function deploy(
    bytes32 salt,
    bytes memory creationCode
  ) external payable returns (address deployed) {
    salt = keccak256(abi.encodePacked(msg.sender, salt));
    return CREATE3.deploy(salt, creationCode, msg.value);
  }

  function getDeployed(address deployer, bytes32 salt) external view returns (address deployed) {
    salt = keccak256(abi.encodePacked(deployer, salt));
    return CREATE3.getDeployed(salt);
  }
}

File 71 of 129 : ERC2771Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.1;

// Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/metatx/ERC2771Context.sol
abstract contract ERC2771Context {
  address private immutable _trustedForwarder;

  constructor(address trustedForwarder) {
    _trustedForwarder = trustedForwarder;
  }

  function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
    return forwarder == _trustedForwarder;
  }

  function _msgSender() internal view virtual returns (address sender) {
    if (isTrustedForwarder(msg.sender)) {
      // The assembly code is more direct than the Solidity version using `abi.decode`
      assembly {
        sender := shr(96, calldataload(sub(calldatasize(), 20)))
      }
    } else {
      return msg.sender;
    }
  }

  function _msgData() internal view virtual returns (bytes calldata) {
    if (isTrustedForwarder(msg.sender)) {
      return msg.data[:msg.data.length - 20];
    } else {
      return msg.data;
    }
  }
}

File 72 of 129 : LiteRoyaltyEngine.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

interface IEIP2981 {
  function royaltyInfo(
    uint256,
    uint256 price
  ) external view returns (address receiver, uint256 amount);
}

// Basic RoyaltyEngine-compliant wrapper around EIP2981
contract LiteRoyaltyEngine {
  function getRoyaltyView(
    address token,
    uint256 tokenId,
    uint256 price
  ) public view returns (address[] memory recipients, uint256[] memory amounts) {
    recipients = new address[](1);
    amounts = new uint256[](1);

    (address recipient, uint256 amount) = IEIP2981(token).royaltyInfo(tokenId, price);
    recipients[0] = recipient;
    amounts[0] = amount;
  }
}

File 73 of 129 : PermitProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {ERC2771Context} from "./ERC2771Context.sol";

import {IEIP2612} from "../interfaces/IEIP2612.sol";
import {IReservoirV6_0_1} from "../interfaces/IReservoirV6_0_1.sol";

// Notes:
// - transfer ERC20 tokens via gasless EIP2612 permits
// - ERC2771-compliant for meta-transaction support

contract PermitProxy is ERC2771Context, ReentrancyGuard {
  using SafeERC20 for IERC20;

  // --- Structs ---

  struct Transfer {
    address recipient;
    uint256 amount;
  }

  // https://eips.ethereum.org/EIPS/eip-2612
  struct EIP2612Permit {
    IERC20 token;
    address owner;
    address spender;
    uint256 amount;
    uint256 deadline;
    uint8 v;
    bytes32 r;
    bytes32 s;
  }

  struct EIP2612PermitWithTransfers {
    EIP2612Permit permit;
    Transfer[] transfers;
  }

  // --- Errors ---

  error Unauthorized();

  // --- Fields ---

  IReservoirV6_0_1 internal immutable ROUTER;

  // --- Constructor ---

  constructor(address router, address trustedForwarder) ERC2771Context(trustedForwarder) {
    ROUTER = IReservoirV6_0_1(router);
  }

  // --- Public methods ---

  function eip2612PermitWithTransfersAndExecute(
    EIP2612PermitWithTransfers[] calldata permitsWithTransfers,
    IReservoirV6_0_1.ExecutionInfo[] calldata executionInfos
  ) external nonReentrant {
    uint256 permitsWithTransfersLength = permitsWithTransfers.length;
    for (uint256 i = 0; i < permitsWithTransfersLength; ) {
      EIP2612PermitWithTransfers memory permitWithTransfers = permitsWithTransfers[i];
      EIP2612Permit memory permit = permitWithTransfers.permit;
      Transfer[] memory transfers = permitWithTransfers.transfers;

      if (permit.owner != _msgSender()) {
        revert Unauthorized();
      }

      IEIP2612(address(permit.token)).permit(
        permit.owner,
        permit.spender,
        permit.amount,
        permit.deadline,
        permit.v,
        permit.r,
        permit.s
      );

      uint256 transfersLength = transfers.length;
      for (uint256 j = 0; j < transfersLength; ) {
        permit.token.safeTransferFrom(permit.owner, transfers[j].recipient, transfers[j].amount);

        unchecked {
          ++j;
        }
      }

      unchecked {
        ++i;
      }
    }

    if (executionInfos.length > 0) {
      ROUTER.execute(executionInfos);
    }
  }

  function eip2612Permit(EIP2612Permit[] calldata permits) external nonReentrant {
    uint256 permitsLength = permits.length;
    for (uint256 i = 0; i < permitsLength; ) {
      EIP2612Permit memory permit = permits[i];

      IEIP2612(address(permit.token)).permit(
        permit.owner,
        permit.spender,
        permit.amount,
        permit.deadline,
        permit.v,
        permit.r,
        permit.s
      );

      unchecked {
        ++i;
      }
    }
  }
}

File 74 of 129 : PunksProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {ICryptoPunksMarket} from "../interfaces/ICryptoPunksMarket.sol";

// Since the CryptoPunks are not ERC721 standard-compliant, you cannot create
// orders for them on modern exchange protocols like Seaport. The workarounds
// include using the wrapped version of CryptoPunks (cumbersome and costly to
// use) or using the native CryptoPunks exchange (it lacks features available
// available when using newer exchange protocols - off-chain orders, bids for
// the whole collection or for a set of attributes). To overcome all of these
// we created a new contract called `PunksProxy` which acts in a similiar way
// to the wrapped version of the CryptoPunks but in a zero-abstraction manner
// with everything abstracted out (eg. no need to wrap or unwrap). It acts as
// a standard ERC721 with the caveat that for any transfer operation there is
// a corresponding CryptoPunks-native approval (basically a private offer for
// a price of zero to the proxy contract).
contract PunksProxy {
  using Address for address;

  // --- Fields ---

  ICryptoPunksMarket public constant EXCHANGE =
    ICryptoPunksMarket(0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB);

  mapping(uint256 => address) private tokenApprovals;
  mapping(address => mapping(address => bool)) private operatorApprovals;

  // --- Errors ---

  error Unauthorized();
  error UnsuccessfulSafeTransfer();

  // --- ERC721 standard events ---

  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);

  // --- ERC721 standard methods ---

  function balanceOf(address owner) external view returns (uint256 balance) {
    balance = EXCHANGE.balanceOf(owner);
  }

  function ownerOf(uint256 tokenId) public view returns (address owner) {
    owner = EXCHANGE.punkIndexToAddress(tokenId);
  }

  function getApproved(uint256 tokenId) public view returns (address approved) {
    approved = tokenApprovals[tokenId];
  }

  function isApprovedForAll(address owner, address operator) public view returns (bool approved) {
    approved = operatorApprovals[owner][operator];
  }

  function approve(address to, uint256 tokenId) external {
    address owner = ownerOf(tokenId);
    if (msg.sender != owner && !isApprovedForAll(owner, msg.sender)) {
      revert Unauthorized();
    }

    tokenApprovals[tokenId] = to;
    emit Approval(owner, to, tokenId);
  }

  function setApprovalForAll(address operator, bool approved) external {
    operatorApprovals[msg.sender][operator] = approved;
    emit ApprovalForAll(msg.sender, operator, approved);
  }

  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId,
    bytes calldata data
  ) external {
    transfer(from, to, tokenId);
    checkOnERC721Received(from, to, tokenId, data);
  }

  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId
  ) external {
    transfer(from, to, tokenId);
    checkOnERC721Received(from, to, tokenId, "");
  }

  function transferFrom(
    address from,
    address to,
    uint256 tokenId
  ) external {
    transfer(from, to, tokenId);
  }

  function transfer(
    address from,
    address to,
    uint256 tokenId
  ) internal {
    address owner = ownerOf(tokenId);
    if (from != owner) {
      revert Unauthorized();
    }

    if (
      msg.sender != owner &&
      getApproved(tokenId) != msg.sender &&
      !isApprovedForAll(owner, msg.sender)
    ) {
      revert Unauthorized();
    }

    EXCHANGE.buyPunk(tokenId);
    EXCHANGE.transferPunk(to, tokenId);
    emit Transfer(from, to, tokenId);
  }

  function checkOnERC721Received(
    address from,
    address to,
    uint256 tokenId,
    bytes memory data
  ) private {
    if (to.isContract()) {
      try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (
        bytes4 result
      ) {
        if (result != IERC721Receiver.onERC721Received.selector) {
          revert UnsuccessfulSafeTransfer();
        }
      } catch {
        revert UnsuccessfulSafeTransfer();
      }
    }
  }
}

File 75 of 129 : ReservoirApprovalProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import {IConduit, IConduitController} from "../interfaces/IConduit.sol";
import {IReservoirV6_0_1} from "../interfaces/IReservoirV6_0_1.sol";

// Forked from:
// https://github.com/ProjectOpenSea/seaport/blob/b13939729001cb12f715d7b73422aafeca0bcd0d/contracts/helpers/TransferHelper.sol
contract ReservoirApprovalProxy is ReentrancyGuard {
  // --- Structs ---

  struct TransferHelperItem {
    IConduit.ConduitItemType itemType;
    address token;
    uint256 identifier;
    uint256 amount;
  }

  struct TransferHelperItemsWithRecipient {
    TransferHelperItem[] items;
    address recipient;
  }

  // --- Errors ---

  error ConduitExecutionFailed();
  error InvalidRecipient();

  // --- Fields ---

  IConduitController internal immutable _CONDUIT_CONTROLLER;
  bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH;
  bytes32 internal immutable _CONDUIT_RUNTIME_CODE_HASH;

  IReservoirV6_0_1 internal immutable _ROUTER;

  // --- Constructor ---

  constructor(address conduitController, address router) {
    IConduitController controller = IConduitController(conduitController);
    (_CONDUIT_CREATION_CODE_HASH, _CONDUIT_RUNTIME_CODE_HASH) = controller.getConduitCodeHashes();

    _CONDUIT_CONTROLLER = controller;
    _ROUTER = IReservoirV6_0_1(router);
  }

  // --- Public methods ---

  function bulkTransferWithExecute(
    TransferHelperItemsWithRecipient[] calldata transfers,
    IReservoirV6_0_1.ExecutionInfo[] calldata executionInfos,
    bytes32 conduitKey
  ) external nonReentrant {
    uint256 numTransfers = transfers.length;

    address conduit = address(
      uint160(
        uint256(
          keccak256(
            abi.encodePacked(
              bytes1(0xff),
              address(_CONDUIT_CONTROLLER),
              conduitKey,
              _CONDUIT_CREATION_CODE_HASH
            )
          )
        )
      )
    );

    uint256 sumOfItemsAcrossAllTransfers;
    unchecked {
      for (uint256 i = 0; i < numTransfers; ++i) {
        TransferHelperItemsWithRecipient calldata transfer = transfers[i];
        sumOfItemsAcrossAllTransfers += transfer.items.length;
      }
    }

    IConduit.ConduitTransfer[] memory conduitTransfers = new IConduit.ConduitTransfer[](
      sumOfItemsAcrossAllTransfers
    );

    uint256 itemIndex;
    unchecked {
      for (uint256 i = 0; i < numTransfers; ++i) {
        TransferHelperItemsWithRecipient calldata transfer = transfers[i];
        TransferHelperItem[] calldata transferItems = transfer.items;

        _checkRecipientIsNotZeroAddress(transfer.recipient);

        uint256 numItemsInTransfer = transferItems.length;
        for (uint256 j = 0; j < numItemsInTransfer; ++j) {
          TransferHelperItem calldata item = transferItems[j];
          conduitTransfers[itemIndex] = IConduit.ConduitTransfer(
            item.itemType,
            item.token,
            msg.sender,
            transfer.recipient,
            item.identifier,
            item.amount
          );

          ++itemIndex;
        }
      }
    }

    bytes4 conduitMagicValue = IConduit(conduit).execute(conduitTransfers);
    if (conduitMagicValue != IConduit.execute.selector) {
      revert ConduitExecutionFailed();
    }

    _ROUTER.execute(executionInfos);
  }

  function _checkRecipientIsNotZeroAddress(address recipient) internal pure {
    if (recipient == address(0x0)) {
      revert InvalidRecipient();
    }
  }
}

File 76 of 129 : TwoStepOwnable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

// Adapted from:
// https://github.com/boringcrypto/BoringSolidity/blob/e74c5b22a61bfbadd645e51a64aa1d33734d577a/contracts/BoringOwnable.sol
contract TwoStepOwnable {
  // --- Fields ---

  address public owner;
  address public pendingOwner;

  // --- Events ---

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  // --- Errors ---

  error InvalidParams();
  error Unauthorized();

  // --- Modifiers ---

  modifier onlyOwner() {
    if (msg.sender != owner) {
      revert Unauthorized();
    }

    _;
  }

  // --- Constructor ---

  constructor(address initialOwner) {
    owner = initialOwner;
    emit OwnershipTransferred(address(0), initialOwner);
  }

  // --- Methods ---

  function transferOwnership(address newOwner) public onlyOwner {
    pendingOwner = newOwner;
  }

  function claimOwnership() public {
    address _pendingOwner = pendingOwner;
    if (msg.sender != _pendingOwner) {
      revert Unauthorized();
    }

    owner = _pendingOwner;
    pendingOwner = address(0);
    emit OwnershipTransferred(owner, _pendingOwner);
  }
}

File 77 of 129 : WETH9.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

contract WETH9 {
  string public name = "Wrapped Ether";
  string public symbol = "WETH";
  uint8 public decimals = 18;

  event Approval(address indexed src, address indexed guy, uint wad);
  event Transfer(address indexed src, address indexed dst, uint wad);
  event Deposit(address indexed dst, uint wad);
  event Withdrawal(address indexed src, uint wad);

  mapping(address => uint) public balanceOf;
  mapping(address => mapping(address => uint)) public allowance;

  receive() external payable {
    deposit();
  }

  function deposit() public payable {
    balanceOf[msg.sender] += msg.value;
    emit Deposit(msg.sender, msg.value);
  }

  function withdraw(uint wad) public {
    require(balanceOf[msg.sender] >= wad);
    balanceOf[msg.sender] -= wad;
    payable(msg.sender).transfer(wad);
    emit Withdrawal(msg.sender, wad);
  }

  function totalSupply() public view returns (uint) {
    return address(this).balance;
  }

  function approve(address guy, uint wad) public returns (bool) {
    allowance[msg.sender][guy] = wad;
    emit Approval(msg.sender, guy, wad);
    return true;
  }

  function transfer(address dst, uint wad) public returns (bool) {
    return transferFrom(msg.sender, dst, wad);
  }

  function transferFrom(address src, address dst, uint wad) public returns (bool) {
    require(balanceOf[src] >= wad);

    if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {
      require(allowance[src][msg.sender] >= wad);
      allowance[src][msg.sender] -= wad;
    }

    balanceOf[src] -= wad;
    balanceOf[dst] += wad;

    emit Transfer(src, dst, wad);

    return true;
  }
}

File 78 of 129 : MockERC1155.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

contract MockERC1155 is ERC1155 {
  uint256 public nextTokenId;

  constructor() ERC1155("https://mock.com") {}

  function mint(uint256 tokenId) external {
    _mint(msg.sender, tokenId, 1, "");
  }

  function mintMany(uint256 tokenId, uint256 amount) external {
    _mint(msg.sender, tokenId, amount, "");
  }

  function mintWithPrice(uint256 amount, uint256 price) external payable {
    require(msg.value == price * amount, "Insufficient value");
    _mint(msg.sender, nextTokenId++, amount, "");
  }

  function fail() external pure {
    revert();
  }
}

File 79 of 129 : MockERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {
  constructor() ERC20("Mock", "MOCK") {}

  function mint(uint256 tokenId) external {
    _mint(msg.sender, tokenId);
  }
}

File 80 of 129 : MockERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract MockERC721 is ERC721 {
  uint256 public nextTokenId;

  constructor() ERC721("Mock", "MOCK") {}

  function mint(uint256 tokenId) external {
    _safeMint(msg.sender, tokenId);
  }

  function mintWithPrice(uint256 price) external payable {
    require(msg.value == price, "Insufficient value");
    _safeMint(msg.sender, nextTokenId++);
  }

  function fail() external pure {
    revert();
  }
}

File 81 of 129 : MockERC721C.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@limitbreak/creator-token-contracts/contracts/access/OwnableBasic.sol";
import "@limitbreak/creator-token-contracts/contracts/erc721c/ERC721C.sol";

contract MockERC721C is OwnableBasic, ERC721C {
  uint256 public nextTokenId;

  constructor() ERC721OpenZeppelin("Mock", "MOCK") {}

  function mint(uint256 tokenId) external {
    _safeMint(msg.sender, tokenId);
  }

  function mintWithPrice(uint256 price) external payable {
    require(msg.value == price, "Insufficient value");
    _safeMint(msg.sender, nextTokenId++);
  }
}

File 82 of 129 : BalanceAssertModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseModule} from "./BaseModule.sol";

// When sniping NFTs, a lot of gas is lost when someone else's fill transaction
// gets included right before. To optimize the amount of gas that is lost, this
// module performs a balance/owner check so that we revert as early as possible
// and spend as few gas as possible.
contract BalanceAssertModule {
  // --- Errors ---

  error AssertFailed();

  // --- [ERC721] Single assert ---

  function assertERC721Owner(
    IERC721 token,
    uint256 tokenId,
    address owner
  ) external view {
    address actualOwner = token.ownerOf(tokenId);
    if (owner != actualOwner) {
      revert AssertFailed();
    }
  }

  // --- [ERC1155] Single assert ---

  function assertERC1155Balance(
    IERC1155 token,
    uint256 tokenId,
    address owner,
    uint256 balance
  ) external view {
    uint256 actualBalance = token.balanceOf(owner, tokenId);
    if (balance < actualBalance) {
      revert AssertFailed();
    }
  }
}

File 83 of 129 : BaseModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {TwoStepOwnable} from "../../misc/TwoStepOwnable.sol";

// Notes:
// - includes common helpers useful for all modules

abstract contract BaseModule is TwoStepOwnable, ReentrancyGuard {
  using SafeERC20 for IERC20;

  // --- Events ---

  event CallExecuted(address target, bytes data, uint256 value);

  // --- Errors ---

  error UnsuccessfulCall();
  error UnsuccessfulPayment();
  error WrongParams();

  // --- Constructor ---

  constructor(address owner) TwoStepOwnable(owner) {}

  // --- Owner ---

  // To be able to recover anything that gets stucked by mistake in the module,
  // we allow the owner to perform any arbitrary call. Since the goal is to be
  // stateless, this should only happen in case of mistakes. In addition, this
  // method is also useful for withdrawing any earned trading rewards.
  function makeCalls(
    address[] calldata targets,
    bytes[] calldata data,
    uint256[] calldata values
  ) external payable onlyOwner nonReentrant {
    uint256 length = targets.length;
    for (uint256 i = 0; i < length; ) {
      _makeCall(targets[i], data[i], values[i]);
      emit CallExecuted(targets[i], data[i], values[i]);

      unchecked {
        ++i;
      }
    }
  }

  // --- Helpers ---

  function _sendETH(address to, uint256 amount) internal {
    if (amount > 0) {
      (bool success, ) = payable(to).call{value: amount}("");
      if (!success) {
        revert UnsuccessfulPayment();
      }
    }
  }

  function _sendERC20(
    address to,
    uint256 amount,
    IERC20 token
  ) internal {
    if (amount > 0) {
      token.safeTransfer(to, amount);
    }
  }

  function _makeCall(
    address target,
    bytes memory data,
    uint256 value
  ) internal {
    (bool success, ) = payable(target).call{value: value}(data);
    if (!success) {
      revert UnsuccessfulCall();
    }
  }
}

File 84 of 129 : AlienswapModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ISeaport} from "../../../interfaces/ISeaport.sol";

// Notes on the Seaport module:
// - supports filling listings (both ERC721/ERC1155)
// - supports filling offers (both ERC721/ERC1155)

contract AlienswapModule is BaseExchangeModule {
  // --- Structs ---

  struct SeaportETHListingWithPrice {
    ISeaport.AdvancedOrder order;
    uint256 price;
  }

  // --- Fields ---

  ISeaport public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = ISeaport(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    ISeaport.AdvancedOrder calldata order,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(
        order,
        new ISeaport.CriteriaResolver[](0),
        params.fillTo,
        params.amount
      )
      : _fillSingleOrder(order, new ISeaport.CriteriaResolver[](0), params.fillTo, params.amount);
  }

  // --- Single ERC20 listing ---

  function acceptERC20Listing(
    ISeaport.AdvancedOrder calldata order,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(
        order,
        new ISeaport.CriteriaResolver[](0),
        params.fillTo,
        0
      )
      : _fillSingleOrder(order, new ISeaport.CriteriaResolver[](0), params.fillTo, 0);
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    SeaportETHListingWithPrice[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillSingleOrderWithRevertIfIncomplete(
          orders[i].order,
          criteriaResolvers,
          params.fillTo,
          orders[i].price
        );

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillSingleOrder(orders[i].order, criteriaResolvers, params.fillTo, orders[i].price);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Multiple ERC20 listings ---

  function acceptERC20Listings(
    ISeaport.AdvancedOrder[] calldata orders,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillSingleOrderWithRevertIfIncomplete(orders[i], criteriaResolvers, params.fillTo, 0);

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillSingleOrder(orders[i], criteriaResolvers, params.fillTo, 0);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Single ERC721 offer ---

  function acceptERC721Offer(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    // Extract the ERC721 token from the consideration items
    ISeaport.ConsiderationItem calldata nftItem = order.parameters.consideration[0];
    if (
      nftItem.itemType != ISeaport.ItemType.ERC721 &&
      nftItem.itemType != ISeaport.ItemType.ERC721_WITH_CRITERIA
    ) {
      revert WrongParams();
    }
    IERC721 nftToken = IERC721(nftItem.token);

    // Extract the payment token from the offer items
    ISeaport.OfferItem calldata paymentItem = order.parameters.offer[0];
    IERC20 paymentToken = IERC20(paymentItem.token);

    // Approve the exchange if needed
    _approveERC721IfNeeded(nftToken, address(EXCHANGE));
    _approveERC20IfNeeded(paymentToken, address(EXCHANGE), type(uint256).max);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(order, criteriaResolvers, address(this), 0)
      : _fillSingleOrder(order, criteriaResolvers, address(this), 0);

    uint256 identifier = nftItem.itemType == ISeaport.ItemType.ERC721
      ? nftItem.identifierOrCriteria
      : criteriaResolvers[0].identifier;

    // Pay fees
    if (nftToken.ownerOf(identifier) != address(this)) {
      // Only pay fees if the fill was successful
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, paymentToken);

        unchecked {
          ++i;
        }
      }
    }

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, nftToken, identifier);

    // Forward any left payment to the specified receiver
    _sendAllERC20(params.fillTo, paymentToken);
  }

  // --- Single ERC1155 offer ---

  function acceptERC1155Offer(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    // Extract the ERC1155 token from the consideration items
    ISeaport.ConsiderationItem calldata nftItem = order.parameters.consideration[0];
    if (
      nftItem.itemType != ISeaport.ItemType.ERC1155 &&
      nftItem.itemType != ISeaport.ItemType.ERC1155_WITH_CRITERIA
    ) {
      revert WrongParams();
    }
    IERC1155 nftToken = IERC1155(nftItem.token);

    // Extract the payment token from the offer items
    ISeaport.OfferItem calldata paymentItem = order.parameters.offer[0];
    IERC20 paymentToken = IERC20(paymentItem.token);

    // Approve the exchange if needed
    _approveERC1155IfNeeded(nftToken, address(EXCHANGE));
    _approveERC20IfNeeded(paymentToken, address(EXCHANGE), type(uint256).max);

    uint256 identifier = nftItem.itemType == ISeaport.ItemType.ERC1155
      ? nftItem.identifierOrCriteria
      : criteriaResolvers[0].identifier;

    uint256 balanceBefore = nftToken.balanceOf(address(this), identifier);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(order, criteriaResolvers, address(this), 0)
      : _fillSingleOrder(order, criteriaResolvers, address(this), 0);

    uint256 balanceAfter = nftToken.balanceOf(address(this), identifier);

    // Pay fees
    uint256 amountFilled = balanceBefore - balanceAfter;
    if (amountFilled > 0) {
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(
          fee.recipient,
          // Only pay fees for the amount that was actually filled
          (fee.amount * amountFilled) / order.numerator,
          paymentToken
        );

        unchecked {
          ++i;
        }
      }
    }

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, nftToken, identifier);

    // Forward any left payment to the specified receiver
    _sendAllERC20(params.fillTo, paymentToken);
  }

  // --- Generic handler (used for Seaport-based approvals) ---

  function matchOrders(
    ISeaport.Order[] calldata orders,
    ISeaport.Fulfillment[] calldata fulfillments
  ) external nonReentrant {
    // We don't perform any kind of input or return value validation,
    // so this function should be used with precaution - the official
    // way to use it is only for Seaport-based approvals
    EXCHANGE.matchOrders(orders, fulfillments);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  // NOTE: In lots of cases, Seaport will not revert if fills were not
  // fully executed. An example of that is partial filling, which will
  // successfully fill any amount that is still available (including a
  // zero amount). One way to ensure that we revert in case of partial
  // executions is to check the order's filled amount before and after
  // we trigger the fill (we can use Seaport's `getOrderStatus` method
  // to check). Since this can be expensive in terms of gas, we have a
  // separate method variant to be called when reverts are enabled.

  function _fillSingleOrder(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    address receiver,
    uint256 value
  ) internal {
    // Execute the fill
    try
      EXCHANGE.fulfillAdvancedOrder{value: value}(order, criteriaResolvers, bytes32(0), receiver)
    {} catch {}
  }

  function _fillSingleOrderWithRevertIfIncomplete(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    address receiver,
    uint256 value
  ) internal {
    // Cache the order's hash
    bytes32 orderHash = _getOrderHash(order.parameters);

    // Before filling, get the order's filled amount
    uint256 beforeFilledAmount = _getFilledAmount(orderHash);

    // Execute the fill
    bool success;
    try
      EXCHANGE.fulfillAdvancedOrder{value: value}(order, criteriaResolvers, bytes32(0), receiver)
    returns (bool fulfilled) {
      success = fulfilled;
    } catch {
      revert UnsuccessfulFill();
    }

    if (!success) {
      revert UnsuccessfulFill();
    } else {
      // After successfully filling, get the order's filled amount
      uint256 afterFilledAmount = _getFilledAmount(orderHash);

      // Make sure the amount filled as part of this call is correct
      if (afterFilledAmount - beforeFilledAmount != order.numerator) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _getOrderHash(
    // Must use `memory` instead of `calldata` for the below cast
    ISeaport.OrderParameters memory orderParameters
  ) internal view returns (bytes32 orderHash) {
    // `OrderParameters` and `OrderComponents` share the exact same
    // fields, apart from the last one, so here we simply treat the
    // `orderParameters` argument as `OrderComponents` and then set
    // the last field to the correct data
    ISeaport.OrderComponents memory orderComponents;
    assembly {
      orderComponents := orderParameters
    }
    orderComponents.counter = EXCHANGE.getCounter(orderParameters.offerer);

    orderHash = EXCHANGE.getOrderHash(orderComponents);
  }

  function _getFilledAmount(bytes32 orderHash) internal view returns (uint256 totalFilled) {
    (, , totalFilled, ) = EXCHANGE.getOrderStatus(orderHash);
  }
}

File 85 of 129 : BaseExchangeModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import {BaseModule} from "../BaseModule.sol";

// Notes:
// - includes common helpers useful for all marketplace/exchange modules

abstract contract BaseExchangeModule is BaseModule {
  using SafeERC20 for IERC20;

  // --- Structs ---

  // Every fill execution has the following parameters:
  // - `fillTo`: the recipient of the received items
  // - `refundTo`: the recipient of any refunds
  // - `revertIfIncomplete`: whether to revert or skip unsuccessful fills

  // The below `ETHListingParams` and `ERC20ListingParams` rely on the
  // off-chain execution encoder to ensure that the orders filled with
  // the passed in listing parameters exactly match (eg. order amounts
  // and payment tokens match).

  struct ETHListingParams {
    address fillTo;
    address refundTo;
    bool revertIfIncomplete;
    // The total amount of ETH to be provided when filling
    uint256 amount;
  }

  struct ERC20ListingParams {
    address fillTo;
    address refundTo;
    bool revertIfIncomplete;
    // The ERC20 payment token for the listings
    IERC20 token;
    // The total amount of `token` to be provided when filling
    uint256 amount;
  }

  struct OfferParams {
    address fillTo;
    address refundTo;
    bool revertIfIncomplete;
  }

  struct Fee {
    address recipient;
    uint256 amount;
  }

  // --- Fields ---

  address public immutable router;

  // --- Errors ---

  error UnsuccessfulFill();

  // --- Constructor ---

  constructor(address routerAddress) {
    router = routerAddress;
  }

  // --- Modifiers ---

  modifier refundETHLeftover(address refundTo) {
    _;

    uint256 leftover = address(this).balance;
    if (leftover > 0) {
      _sendETH(refundTo, leftover);
    }
  }

  modifier refundERC20Leftover(address refundTo, IERC20 token) {
    _;

    uint256 leftover = token.balanceOf(address(this));
    if (leftover > 0) {
      token.safeTransfer(refundTo, leftover);
    }
  }

  modifier chargeETHFees(Fee[] calldata fees, uint256 amount) {
    if (fees.length == 0) {
      _;
    } else {
      uint256 balanceBefore = address(this).balance;

      _;

      uint256 length = fees.length;
      if (length > 0) {
        uint256 balanceAfter = address(this).balance;
        uint256 actualPaid = balanceBefore - balanceAfter;

        uint256 actualFee;
        for (uint256 i = 0; i < length; ) {
          // Adjust the fee to what was actually paid
          actualFee = (fees[i].amount * actualPaid) / amount;
          if (actualFee > 0) {
            _sendETH(fees[i].recipient, actualFee);
          }

          unchecked {
            ++i;
          }
        }
      }
    }
  }

  modifier chargeERC20Fees(
    Fee[] calldata fees,
    IERC20 token,
    uint256 amount
  ) {
    if (fees.length == 0) {
      _;
    } else {
      uint256 balanceBefore = token.balanceOf(address(this));

      _;

      uint256 length = fees.length;
      if (length > 0) {
        uint256 balanceAfter = token.balanceOf(address(this));
        uint256 actualPaid = balanceBefore - balanceAfter;

        uint256 actualFee;
        for (uint256 i = 0; i < length; ) {
          // Adjust the fee to what was actually paid
          actualFee = (fees[i].amount * actualPaid) / amount;
          if (actualFee > 0) {
            token.safeTransfer(fees[i].recipient, actualFee);
          }

          unchecked {
            ++i;
          }
        }
      }
    }
  }

  // --- Helpers ---

  function _sendAllETH(address to) internal {
    _sendETH(to, address(this).balance);
  }

  function _sendAllERC20(address to, IERC20 token) internal {
    uint256 balance = token.balanceOf(address(this));
    if (balance > 0) {
      token.safeTransfer(to, balance);
    }
  }

  function _sendAllERC721(address to, IERC721 token, uint256 tokenId) internal {
    if (token.ownerOf(tokenId) == address(this)) {
      token.safeTransferFrom(address(this), to, tokenId);
    }
  }

  function _sendAllERC1155(address to, IERC1155 token, uint256 tokenId) internal {
    uint256 balance = token.balanceOf(address(this), tokenId);
    if (balance > 0) {
      token.safeTransferFrom(address(this), to, tokenId, balance, "");
    }
  }

  function _approveERC20IfNeeded(IERC20 token, address spender, uint256 amount) internal {
    uint256 allowance = token.allowance(address(this), spender);
    if (allowance < amount) {
      token.approve(spender, amount);
    }
  }

  function _approveERC721IfNeeded(IERC721 token, address operator) internal {
    bool isApproved = token.isApprovedForAll(address(this), operator);
    if (!isApproved) {
      token.setApprovalForAll(operator, true);
    }
  }

  function _approveERC1155IfNeeded(IERC1155 token, address operator) internal {
    bool isApproved = token.isApprovedForAll(address(this), operator);
    if (!isApproved) {
      token.setApprovalForAll(operator, true);
    }
  }
}

File 86 of 129 : CaviarV1Module.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ICaviarPoolV1} from "../../../interfaces/ICaviarV1.sol";

contract CaviarV1Module is BaseExchangeModule {
  // --- Constructor ---

  constructor(address owner, address router) BaseModule(owner) BaseExchangeModule(router) {}

  // --- Fallback ---

  receive() external payable {}

  // --- Multiple ETH listings ---

  function buyWithETH(
    ICaviarPoolV1[] calldata pairs,
    uint256[] calldata nftIds,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256[] memory tokenIds = new uint256[](1);
    uint256 pairsLength = pairs.length;
    for (uint256 i; i < pairsLength; ) {
      // Fetch the current price
      uint256 inputAmount = pairs[i].buyQuote(1e18);
      uint256 tokenId = nftIds[i];
      tokenIds[0] = tokenId;

      // Execute fill
      try pairs[i].nftBuy{value: inputAmount}(tokenIds, inputAmount, 0) {
        _sendAllERC721(params.fillTo, IERC721(pairs[i].nft()), tokenId);
      } catch {
        if (params.revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  // --- Single offer ---

  function sell(
    ICaviarPoolV1 pair,
    uint256 nftId,
    uint256 minOutput,
    ICaviarPoolV1.Message calldata stolenProof,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    address nft = pair.nft();

    // Approve the pair if needed
    _approveERC721IfNeeded(IERC721(nft), address(pair));

    // Build router data
    uint256[] memory tokenIds = new uint256[](1);
    bytes32[][] memory proofs = new bytes32[][](0);
    ICaviarPoolV1.Message[] memory stolenProofs = new ICaviarPoolV1.Message[](1);
    tokenIds[0] = nftId;
    stolenProofs[0] = stolenProof;

    // Execute fill
    try pair.nftSell(tokenIds, minOutput, 0, proofs, stolenProofs) {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendETH(fee.recipient, fee.amount);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllETH(params.fillTo);
    } catch {
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any leftovers
    _sendAllERC721(params.refundTo, IERC721(nft), nftId);
  }

  // --- ERC721/1155 hooks ---

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }
}

File 87 of 129 : CryptoPunksModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ICryptoPunksMarket} from "../../../interfaces/ICryptoPunksMarket.sol";

contract CryptoPunksModule is BaseExchangeModule {
  // --- Fields ---

  ICryptoPunksMarket public immutable MARKETPLACE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address marketplace
  ) BaseModule(owner) BaseExchangeModule(router) {
    MARKETPLACE = ICryptoPunksMarket(marketplace);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Multiple buy Punks ---

  function batchBuyPunksWithETH(
    ICryptoPunksMarket.BuyOrder[] calldata buyOrders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = buyOrders.length;
    for (uint256 i = 0; i < length; ) {
      // Execute fill
      _buy(buyOrders[i], params.revertIfIncomplete);

      unchecked {
        ++i;
      }
    }
  }

  function _buy(ICryptoPunksMarket.BuyOrder calldata buyOrder, bool revertIfIncomplete) internal {
    try MARKETPLACE.buyPunk{value: buyOrder.price}(buyOrder.punkIndex) {
      // Transfer the punk to the receiver
      MARKETPLACE.transferPunk(buyOrder.buyer, buyOrder.punkIndex);
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 88 of 129 : DittoModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";

import {IDittoPool} from "../../../interfaces/IDittoPool.sol";

struct DittoOrderParams {
  address tokenSender;
  uint256[] nftIds;
  bytes swapData;
}

contract DittoModule is BaseExchangeModule {
  using SafeERC20 for IERC20;

  // --- Constructor ---

  constructor(address owner, address router) BaseModule(owner) BaseExchangeModule(router) {}

  // --- Helper methods ---

  function poolTransferNftFrom(IERC721 token, address from, address to, uint256 tokenId) external {
    token.transferFrom(from, to, tokenId);
  }

  function poolTransferErc20From(
    IERC20 token,
    address from,
    address to,
    uint256 amount
  ) external virtual {
    if (from == address(this)) {
      token.safeTransfer(to, amount);
    } else {
      token.safeTransferFrom(from, to, amount);
    }
  }

  // --- Multiple ERC20 listing ---

  function buyWithERC20(
    IDittoPool[] calldata pairs,
    DittoOrderParams[] calldata orderParams,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    uint256 pairsLength = pairs.length;
    for (uint256 i; i < pairsLength; ) {
      // Execute fill
      IDittoPool.SwapTokensForNftsArgs memory args = IDittoPool.SwapTokensForNftsArgs({
        nftIds: orderParams[i].nftIds,
        maxExpectedTokenInput: params.amount,
        tokenSender: orderParams[i].tokenSender,
        nftRecipient: params.fillTo,
        swapData: orderParams[i].swapData
      });

      pairs[i].swapTokensForNfts(args);

      unchecked {
        ++i;
      }
    }
  }

  // --- Single ERC721 offer ---

  function sell(
    IDittoPool pool,
    DittoOrderParams calldata orderParams,
    uint256[] calldata lpIds,
    bytes calldata permitterData,
    uint256 minOutput,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    IERC20 token = pool.token();

    IDittoPool.SwapNftsForTokensArgs memory args = IDittoPool.SwapNftsForTokensArgs({
      nftIds: orderParams.nftIds,
      lpIds: lpIds,
      minExpectedTokenOutput: minOutput,
      nftSender: orderParams.tokenSender,
      tokenRecipient: params.fillTo,
      permitterData: permitterData,
      swapData: orderParams.swapData
    });

    pool.swapNftsForTokens(args);

    // Pay fees
    uint256 feesLength = fees.length;
    for (uint256 i; i < feesLength; ) {
      Fee memory fee = fees[i];
      _sendERC20(fee.recipient, fee.amount, token);

      unchecked {
        ++i;
      }
    }
  }
}

File 89 of 129 : ElementModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {IElement} from "../../../interfaces/IElement.sol";

// Notes:
// - supports filling listings (both ERC721/ERC1155)
// - supports filling offers (both ERC721/ERC1155)

contract ElementModule is BaseExchangeModule {
  using SafeERC20 for IERC20;

  // --- Fields ---

  IElement public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = IElement(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- [ERC721] Single ETH listing ---

  function acceptETHListingERC721(
    IElement.NFTSellOrder calldata order,
    IElement.Signature calldata signature,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buyERC721Ex(order, signature, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- [ERC721] Single ERC20 listing ---

  function acceptERC20ListingERC721(
    IElement.NFTSellOrder calldata order,
    IElement.Signature calldata signature,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _buyERC721Ex(order, signature, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC721] Multiple ETH listings ---

  function acceptETHListingsERC721(
    IElement.NFTSellOrder[] calldata orders,
    IElement.Signature[] calldata signatures,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buyERC721sEx(orders, signatures, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- [ERC721] Multiple ERC20 listings ---

  function acceptERC20ListingsERC721(
    IElement.NFTSellOrder[] calldata orders,
    IElement.Signature[] calldata signatures,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _buyERC721sEx(orders, signatures, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC721] Single ETH listing V2 ---

  function acceptETHListingERC721V2(
    IElement.BatchSignedOrder calldata order,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _fillBatchSignedOrder(order, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- [ERC721] Single ERC20 listing V2 ---

  function acceptERC20ListingERC721V2(
    IElement.BatchSignedOrder calldata order,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _fillBatchSignedOrder(order, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC721] Multiple ETH listings V2 ---

  function acceptETHListingsERC721V2(
    IElement.BatchSignedOrder[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _fillBatchSignedOrders(orders, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- [ERC721] Multiple ERC20 listings V2 ---

  function acceptERC20ListingsERC721V2(
    IElement.BatchSignedOrder[] calldata orders,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _fillBatchSignedOrders(orders, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC1155] Single ETH listing ---

  function acceptETHListingERC1155(
    IElement.ERC1155SellOrder calldata order,
    IElement.Signature calldata signature,
    uint128 amount,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buyERC1155Ex(
      order,
      signature,
      amount,
      params.fillTo,
      params.revertIfIncomplete,
      params.amount
    );
  }

  // --- [ERC1155] Single ERC20 listing ---

  function acceptERC20ListingERC1155(
    IElement.ERC1155SellOrder calldata order,
    IElement.Signature calldata signature,
    uint128 amount,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _buyERC1155Ex(order, signature, amount, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC1155] Multiple ETH listings ---

  function acceptETHListingsERC1155(
    IElement.ERC1155SellOrder[] calldata orders,
    IElement.Signature[] calldata signatures,
    uint128[] calldata amounts,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buyERC1155sEx(
      orders,
      signatures,
      amounts,
      params.fillTo,
      params.revertIfIncomplete,
      params.amount
    );
  }

  // --- [ERC1155] Multiple ERC20 listings ---

  function acceptERC20ListingsERC1155(
    IElement.ERC1155SellOrder[] calldata orders,
    IElement.Signature[] calldata signatures,
    uint128[] calldata amounts,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _buyERC1155sEx(orders, signatures, amounts, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC721] Single offer ---

  function acceptERC721Offer(
    IElement.NFTBuyOrder calldata order,
    IElement.Signature calldata signature,
    OfferParams calldata params,
    uint256 tokenId,
    Fee[] calldata fees
  ) external nonReentrant {
    // Approve the exchange if needed
    _approveERC721IfNeeded(IERC721(order.nft), address(EXCHANGE));

    // Execute fill
    try EXCHANGE.sellERC721(order, signature, tokenId, false, "") {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, order.erc20Token);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(params.fillTo, order.erc20Token);
    } catch {
      // Revert if specified
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, IERC721(order.nft), tokenId);
  }

  // --- [ERC1155] Single offer ---

  function acceptERC1155Offer(
    IElement.ERC1155BuyOrder calldata order,
    IElement.Signature calldata signature,
    uint128 amount,
    OfferParams calldata params,
    uint256 tokenId,
    Fee[] calldata fees
  ) external nonReentrant {
    // Approve the exchange if needed
    _approveERC1155IfNeeded(IERC1155(order.erc1155Token), address(EXCHANGE));

    // Execute fill
    try EXCHANGE.sellERC1155(order, signature, tokenId, amount, false, "") {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, order.erc20Token);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(params.fillTo, order.erc20Token);
    } catch {
      // Revert if specified
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, IERC1155(order.erc1155Token), tokenId);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  function _buyERC721Ex(
    IElement.NFTSellOrder calldata order,
    IElement.Signature calldata signature,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute fill
    try EXCHANGE.buyERC721Ex{value: value}(order, signature, receiver, "") {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _buyERC721sEx(
    IElement.NFTSellOrder[] calldata orders,
    IElement.Signature[] calldata signatures,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    uint256 length = orders.length;

    address[] memory takers = new address[](length);
    for (uint256 i; i < length; ) {
      takers[i] = receiver;
      unchecked {
        ++i;
      }
    }

    // Execute fill
    try
      EXCHANGE.batchBuyERC721sEx{value: value}(
        orders,
        signatures,
        takers,
        new bytes[](length),
        revertIfIncomplete
      )
    {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _fillBatchSignedOrder(
    IElement.BatchSignedOrder calldata order,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    IElement.Parameter memory parameter;
    parameter.r = order.r;
    parameter.s = order.s;

    // data1 [56 bits(startNonce) + 8 bits(v) + 32 bits(listingTime) + 160 bits(maker)]
    parameter.data1 =
      (order.startNonce << 200) |
      (uint256(order.v) << 192) |
      (order.listingTime << 160) |
      uint256(uint160(order.maker));

    // data2 [64 bits(taker part1) + 32 bits(expiryTime) + 160 bits(erc20Token)]
    uint256 taker = uint256(uint160(receiver));
    parameter.data2 =
      ((taker >> 96) << 192) |
      (order.expirationTime << 160) |
      uint256(uint160(order.erc20Token));

    // data3 [96 bits(taker part2) + 160 bits(platformFeeRecipient)]
    parameter.data3 = (taker << 160) | uint256(uint160(order.platformFeeRecipient));

    // Execute fill
    try
      EXCHANGE.fillBatchSignedERC721Order{value: value}(parameter, order.collectionsBytes)
    {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _fillBatchSignedOrders(
    IElement.BatchSignedOrder[] calldata orders,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    uint256 length = orders.length;
    uint256 taker = uint256(uint160(receiver));

    IElement.Parameters[] memory parameters = new IElement.Parameters[](length);
    for (uint256 i; i < length; ) {
      IElement.BatchSignedOrder calldata order = orders[i];

      IElement.Parameters memory parameter;
      parameter.r = order.r;
      parameter.s = order.s;
      parameter.collections = order.collectionsBytes;

      // data1 [56 bits(startNonce) + 8 bits(v) + 32 bits(listingTime) + 160 bits(maker)]
      parameter.data1 =
        (order.startNonce << 200) |
        (uint256(order.v) << 192) |
        (order.listingTime << 160) |
        uint256(uint160(order.maker));

      // data2 [64 bits(taker part1) + 32 bits(expiryTime) + 160 bits(erc20Token)]
      parameter.data2 =
        ((taker >> 96) << 192) |
        (order.expirationTime << 160) |
        uint256(uint160(order.erc20Token));

      // data3 [96 bits(taker part2) + 160 bits(platformFeeRecipient)]
      parameter.data3 = (taker << 160) | uint256(uint160(order.platformFeeRecipient));

      parameters[i] = parameter;
      unchecked {
        ++i;
      }
    }

    // Execute fill
    uint256 additional2 = revertIfIncomplete ? (1 << 248) : 0;
    try EXCHANGE.fillBatchSignedERC721Orders{value: value}(parameters, 0, additional2) {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _buyERC1155Ex(
    IElement.ERC1155SellOrder calldata order,
    IElement.Signature calldata signature,
    uint128 amount,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    try EXCHANGE.buyERC1155Ex{value: value}(order, signature, receiver, amount, "") {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _buyERC1155sEx(
    IElement.ERC1155SellOrder[] calldata orders,
    IElement.Signature[] calldata signatures,
    uint128[] calldata amounts,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    uint256 length = orders.length;

    address[] memory takers = new address[](length);
    for (uint256 i; i < length; ) {
      takers[i] = receiver;
      unchecked {
        ++i;
      }
    }

    // Execute fill
    try
      EXCHANGE.batchBuyERC1155sEx{value: value}(
        orders,
        signatures,
        takers,
        amounts,
        new bytes[](length),
        revertIfIncomplete
      )
    {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 90 of 129 : FoundationModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {IFoundation} from "../../../interfaces/IFoundation.sol";

// Notes:
// - only supports filling "buy now" listings (ERC721 and ETH-denominated)

contract FoundationModule is BaseExchangeModule {
  // --- Structs ---

  struct Listing {
    IERC721 token;
    uint256 tokenId;
    uint256 price;
  }

  // --- Fields ---

  IFoundation public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = IFoundation(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    Listing calldata listing,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buy(listing.token, listing.tokenId, params.fillTo, params.revertIfIncomplete, listing.price);
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    Listing[] calldata listings,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Foundation does not support batch filling so we fill orders one by one
    for (uint256 i = 0; i < listings.length; ) {
      _buy(
        listings[i].token,
        listings[i].tokenId,
        params.fillTo,
        params.revertIfIncomplete,
        listings[i].price
      );

      unchecked {
        ++i;
      }
    }
  }

  // --- ERC721 hooks ---

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata // data
  ) external pure returns (bytes4) {
    return this.onERC721Received.selector;
  }

  // --- Internal ---

  function _buy(
    IERC721 token,
    uint256 tokenId,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute fill
    try EXCHANGE.buyV2{value: value}(token, tokenId, value, receiver) {
      token.safeTransferFrom(address(this), receiver, tokenId);
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 91 of 129 : LooksRareModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ILooksRare, ILooksRareTransferSelectorNFT} from "../../../interfaces/ILooksRare.sol";

// Notes:
// - supports filling listings (both ERC721/ERC1155 but only ETH-denominated)
// - supports filling offers (both ERC721/ERC1155)

contract LooksRareModule is BaseExchangeModule {
  using SafeERC20 for IERC20;

  // --- Fields ---

  ILooksRare public immutable EXCHANGE;

  address public immutable ERC721_TRANSFER_MANAGER;
  address public immutable ERC1155_TRANSFER_MANAGER;

  bytes4 public constant ERC721_INTERFACE = 0x80ac58cd;
  bytes4 public constant ERC1155_INTERFACE = 0xd9b67a26;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = ILooksRare(exchange);

    ILooksRareTransferSelectorNFT transferSelector = EXCHANGE.transferSelectorNFT();
    ERC721_TRANSFER_MANAGER = transferSelector.TRANSFER_MANAGER_ERC721();
    ERC1155_TRANSFER_MANAGER = transferSelector.TRANSFER_MANAGER_ERC1155();
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    ILooksRare.TakerOrder calldata takerBid,
    ILooksRare.MakerOrder calldata makerAsk,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buy(takerBid, makerAsk, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    ILooksRare.TakerOrder[] calldata takerBids,
    ILooksRare.MakerOrder[] calldata makerAsks,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // LooksRare does not support batch filling so we fill orders one by one
    for (uint256 i = 0; i < takerBids.length; ) {
      // Use `memory` to avoid `Stack too deep` errors
      ILooksRare.TakerOrder memory takerBid = takerBids[i];

      // Execute fill
      _buy(takerBids[i], makerAsks[i], params.fillTo, params.revertIfIncomplete, takerBid.price);

      unchecked {
        ++i;
      }
    }
  }

  // --- [ERC721] Single offer ---

  function acceptERC721Offer(
    ILooksRare.TakerOrder calldata takerAsk,
    ILooksRare.MakerOrder calldata makerBid,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    IERC721 collection = IERC721(address(makerBid.collection));

    // Approve the transfer manager if needed
    _approveERC721IfNeeded(collection, ERC721_TRANSFER_MANAGER);

    // Execute the fill
    _sell(takerAsk, makerBid, params.fillTo, params.revertIfIncomplete, fees);

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, collection, takerAsk.tokenId);
  }

  // --- [ERC1155] Single offer ---

  function acceptERC1155Offer(
    ILooksRare.TakerOrder calldata takerAsk,
    ILooksRare.MakerOrder calldata makerBid,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    IERC1155 collection = IERC1155(address(makerBid.collection));

    // Approve the transfer manager if needed
    _approveERC1155IfNeeded(collection, ERC1155_TRANSFER_MANAGER);

    // Execute the fill
    _sell(takerAsk, makerBid, params.fillTo, params.revertIfIncomplete, fees);

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, collection, takerAsk.tokenId);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  function _buy(
    ILooksRare.TakerOrder calldata takerBid,
    ILooksRare.MakerOrder calldata makerAsk,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute the fill
    try EXCHANGE.matchAskWithTakerBidUsingETHAndWETH{value: value}(takerBid, makerAsk) {
      IERC165 collection = makerAsk.collection;

      // Forward any token to the specified receiver
      bool isERC721 = collection.supportsInterface(ERC721_INTERFACE);
      if (isERC721) {
        IERC721(address(collection)).safeTransferFrom(address(this), receiver, takerBid.tokenId);
      } else {
        bool isERC1155 = collection.supportsInterface(ERC1155_INTERFACE);
        if (isERC1155) {
          IERC1155(address(collection)).safeTransferFrom(
            address(this),
            receiver,
            takerBid.tokenId,
            makerAsk.amount,
            ""
          );
        }
      }
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _sell(
    ILooksRare.TakerOrder calldata takerAsk,
    ILooksRare.MakerOrder calldata makerBid,
    address receiver,
    bool revertIfIncomplete,
    Fee[] calldata fees
  ) internal {
    // Execute the fill
    try EXCHANGE.matchBidWithTakerAsk(takerAsk, makerBid) {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, makerBid.currency);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(receiver, makerBid.currency);
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 92 of 129 : LooksRareV2Module.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ILooksRareV2, ITransferManager} from "../../../interfaces/ILooksRareV2.sol";

// Notes:
// - supports filling listings (both ERC721/ERC1155 but only ETH-denominated)
// - supports filling offers (both ERC721/ERC1155)

contract LooksRareV2Module is BaseExchangeModule {
  using SafeERC20 for IERC20;

  // --- Fields ---

  ILooksRareV2 public immutable EXCHANGE;

  ITransferManager public immutable TRANSFER_MANAGER;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = ILooksRareV2(exchange);
    TRANSFER_MANAGER = EXCHANGE.transferManager();

    // Grant approval to the transfer manager
    address[] memory operators = new address[](1);
    operators[0] = address(EXCHANGE);
    TRANSFER_MANAGER.grantApprovals(operators);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    ILooksRareV2.MakerOrder calldata makerAsk,
    bytes calldata makerSignature,
    ILooksRareV2.MerkleTree calldata merkleTree,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buy(
      makerAsk,
      makerSignature,
      merkleTree,
      params.fillTo,
      params.revertIfIncomplete,
      params.amount
    );
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    ILooksRareV2.MakerOrder[] calldata makerAsks,
    bytes[] calldata makerSignatures,
    ILooksRareV2.MerkleTree[] calldata merkleTrees,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // LooksRare does not support batch filling so we fill orders one by one
    for (uint256 i = 0; i < makerAsks.length; ) {
      // Execute fill
      _buy(
        makerAsks[i],
        makerSignatures[i],
        merkleTrees[i],
        params.fillTo,
        params.revertIfIncomplete,
        makerAsks[i].price
      );

      unchecked {
        ++i;
      }
    }
  }

  // --- [ERC721] Single offer ---

  function acceptERC721Offer(
    ILooksRareV2.MakerOrder calldata makerBid,
    bytes calldata takerAdditionalParameters,
    bytes calldata makerSignature,
    ILooksRareV2.MerkleTree calldata merkleTree,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    IERC721 collection = IERC721(address(makerBid.collection));

    // Approve the transfer manager if needed
    _approveERC721IfNeeded(collection, address(TRANSFER_MANAGER));

    // Execute the fill
    uint256 tokenId = _sell(
      makerBid,
      takerAdditionalParameters,
      makerSignature,
      merkleTree,
      params.fillTo,
      params.revertIfIncomplete,
      fees
    );

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, collection, tokenId);
  }

  // --- [ERC1155] Single offer ---

  function acceptERC1155Offer(
    ILooksRareV2.MakerOrder calldata makerBid,
    bytes calldata takerAdditionalParameters,
    bytes calldata makerSignature,
    ILooksRareV2.MerkleTree calldata merkleTree,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    IERC1155 collection = IERC1155(address(makerBid.collection));

    // Approve the transfer manager if needed
    _approveERC1155IfNeeded(collection, address(TRANSFER_MANAGER));

    // Execute the fill
    uint256 tokenId = _sell(
      makerBid,
      takerAdditionalParameters,
      makerSignature,
      merkleTree,
      params.fillTo,
      params.revertIfIncomplete,
      fees
    );

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, collection, tokenId);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  function _buy(
    ILooksRareV2.MakerOrder calldata makerAsk,
    bytes calldata makerSignature,
    ILooksRareV2.MerkleTree calldata merkleTree,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    ILooksRareV2.TakerOrder memory takerBid;
    takerBid.recipient = receiver;

    // Execute the fill
    try
      EXCHANGE.executeTakerBid{value: value}(
        takerBid,
        makerAsk,
        makerSignature,
        merkleTree,
        address(0)
      )
    {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _sell(
    ILooksRareV2.MakerOrder calldata makerBid,
    bytes calldata takerAdditionalParameters,
    bytes calldata makerSignature,
    ILooksRareV2.MerkleTree calldata merkleTree,
    address receiver,
    bool revertIfIncomplete,
    Fee[] calldata fees
  ) internal returns (uint256 tokenId) {
    ILooksRareV2.TakerOrder memory takerAsk;
    takerAsk.recipient = address(this);
    takerAsk.additionalParameters = takerAdditionalParameters;

    // Execute the fill
    try EXCHANGE.executeTakerAsk(takerAsk, makerBid, makerSignature, merkleTree, address(0)) {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, makerBid.currency);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(receiver, makerBid.currency);
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    tokenId = abi.decode(takerAdditionalParameters[0:32], (uint256));
  }
}

File 93 of 129 : NFTXModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";

import {INFTXMarketplaceZap} from "../../../interfaces/INFTX.sol";
import {INFTXVault} from "../../../interfaces/INFTXVault.sol";
import {INFTXVaultFactory} from "../../../interfaces/INFTXVaultFactory.sol";

contract NFTXModule is BaseExchangeModule {
  // --- Fields ---

  INFTXMarketplaceZap public immutable NFTX_MARKETPLACE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address nftxMarketplace
  ) BaseModule(owner) BaseExchangeModule(router) {
    NFTX_MARKETPLACE = INFTXMarketplaceZap(nftxMarketplace);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Multiple ETH listings ---

  function buyWithETH(
    INFTXMarketplaceZap.BuyOrder[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = orders.length;
    for (uint256 i = 0; i < length; ) {
      INFTXMarketplaceZap.BuyOrder memory order = orders[i];
      // Execute fill
      _buy(orders[i], params.fillTo, params.revertIfIncomplete, order.price);

      unchecked {
        ++i;
      }
    }
  }

  // --- Internal ---

  function _buy(
    INFTXMarketplaceZap.BuyOrder calldata buyOrder,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute the fill
    try
      NFTX_MARKETPLACE.buyAndRedeem{value: value}(
        buyOrder.vaultId,
        buyOrder.amount,
        buyOrder.specificIds,
        buyOrder.path,
        receiver
      )
    {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  // --- Single ERC721 offer ---

  function sell(
    INFTXMarketplaceZap.SellOrder[] calldata orders,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    uint256 length = orders.length;
    for (uint256 i = 0; i < length; ) {
      // Execute fill
      _sell(orders[i], params.fillTo, params.revertIfIncomplete, fees);

      unchecked {
        ++i;
      }
    }
  }

  function _sell(
    INFTXMarketplaceZap.SellOrder calldata sellOrder,
    address receiver,
    bool revertIfIncomplete,
    Fee[] calldata fees
  ) internal {
    address collection = sellOrder.collection;

    INFTXVault vault = INFTXVault(NFTX_MARKETPLACE.nftxFactory().vault(sellOrder.vaultId));

    // Execute the sell
    if (!vault.is1155()) {
      _approveERC721IfNeeded(IERC721(collection), address(NFTX_MARKETPLACE));

      try
        NFTX_MARKETPLACE.mintAndSell721WETH(
          sellOrder.vaultId,
          sellOrder.specificIds,
          sellOrder.price,
          sellOrder.path,
          address(this)
        )
      {
        // Pay fees
        uint256 feesLength = fees.length;
        for (uint256 i; i < feesLength; ) {
          Fee memory fee = fees[i];
          _sendERC20(fee.recipient, fee.amount, sellOrder.currency);
          unchecked {
            ++i;
          }
        }

        // Forward any left payment to the specified receiver
        _sendAllERC20(receiver, sellOrder.currency);
      } catch {
        // Revert if specified
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      // Refund any ERC721 leftover
      uint256 length = sellOrder.specificIds.length;
      for (uint256 i = 0; i < length; ) {
        _sendAllERC721(receiver, IERC721(collection), sellOrder.specificIds[i]);

        unchecked {
          ++i;
        }
      }
    } else {
      _approveERC1155IfNeeded(IERC1155(collection), address(NFTX_MARKETPLACE));

      try
        NFTX_MARKETPLACE.mintAndSell1155WETH(
          sellOrder.vaultId,
          sellOrder.specificIds,
          sellOrder.amounts,
          sellOrder.price,
          sellOrder.path,
          address(this)
        )
      {
        // Pay fees
        uint256 feesLength = fees.length;
        for (uint256 i; i < feesLength; ) {
          Fee memory fee = fees[i];
          _sendERC20(fee.recipient, fee.amount, sellOrder.currency);
          unchecked {
            ++i;
          }
        }

        // Forward any left payment to the specified receiver
        _sendAllERC20(receiver, sellOrder.currency);
      } catch {
        // Revert if specified
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      // Refund any ERC1155 leftover
      uint256 length = sellOrder.specificIds.length;
      for (uint256 i = 0; i < length; ) {
        _sendAllERC1155(receiver, IERC1155(collection), sellOrder.specificIds[i]);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }
}

File 94 of 129 : NFTXV3Module.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";

import {INFTXV3MarketplaceZap} from "../../../interfaces/INFTXV3MarketplaceZap.sol";
import {INFTXVault} from "../../../interfaces/INFTXVault.sol";
import {INFTXVaultFactory} from "../../../interfaces/INFTXVaultFactory.sol";

contract NFTXV3Module is BaseExchangeModule {
  // --- Fields ---

  INFTXV3MarketplaceZap public immutable NFTX_V3_MARKETPLACE;

  bytes4 public constant ERC721_INTERFACE = 0x80ac58cd;
  bytes4 public constant ERC1155_INTERFACE = 0xd9b67a26;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address nftxMarketplace
  ) BaseModule(owner) BaseExchangeModule(router) {
    NFTX_V3_MARKETPLACE = INFTXV3MarketplaceZap(nftxMarketplace);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Multiple ETH listings ---

  function buyWithETH(
    INFTXV3MarketplaceZap.BuyOrder[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = orders.length;
    for (uint256 i = 0; i < length; ) {
      INFTXV3MarketplaceZap.BuyOrder memory order = orders[i];

      // Execute fill
      _buy(orders[i], params.fillTo, params.revertIfIncomplete, order.price);

      unchecked {
        ++i;
      }
    }
  }

  // --- Internal ---

  function _buy(
    INFTXV3MarketplaceZap.BuyOrder calldata buyOrder,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute the fill
    try
      NFTX_V3_MARKETPLACE.buyNFTsWithETH{value: value}(
        buyOrder.vaultId,
        buyOrder.idsOut,
        buyOrder.executeCallData,
        payable(receiver),
        buyOrder.vTokenPremiumLimit,
        buyOrder.deductRoyalty
      )
    {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  // --- Single ERC721 offer ---

  function sell(
    INFTXV3MarketplaceZap.SellOrder[] calldata orders,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    uint256 length = orders.length;
    for (uint256 i = 0; i < length; ) {
      // Execute fill
      _sell(orders[i], params.fillTo, params.revertIfIncomplete, fees);

      unchecked {
        ++i;
      }
    }
  }

  function _sell(
    INFTXV3MarketplaceZap.SellOrder calldata sellOrder,
    address receiver,
    bool revertIfIncomplete,
    Fee[] calldata fees
  ) internal {
    address collection = sellOrder.collection;

    INFTXVault vault = INFTXVault(NFTX_V3_MARKETPLACE.nftxVaultFactory().vault(sellOrder.vaultId));

    // Execute the sell
    if (!vault.is1155()) {
      _approveERC721IfNeeded(IERC721(collection), address(NFTX_V3_MARKETPLACE));

      // Return ETH
      try
        NFTX_V3_MARKETPLACE.sell721(
          sellOrder.vaultId,
          sellOrder.idsIn,
          sellOrder.executeCallData,
          payable(address(this)),
          sellOrder.deductRoyalty
        )
      {
        // Pay fees
        uint256 feesLength = fees.length;
        for (uint256 i; i < feesLength; ) {
          Fee memory fee = fees[i];
          _sendETH(fee.recipient, fee.amount);

          unchecked {
            ++i;
          }
        }

        // Forward any left payment to the specified receiver
        _sendAllETH(receiver);
      } catch {
        // Revert if specified
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      // Refund any ERC721 leftover
      uint256 length = sellOrder.idsIn.length;
      for (uint256 i = 0; i < length; ) {
        _sendAllERC721(receiver, IERC721(collection), sellOrder.idsIn[i]);

        unchecked {
          ++i;
        }
      }
    } else {
      _approveERC1155IfNeeded(IERC1155(collection), address(NFTX_V3_MARKETPLACE));

      try
        NFTX_V3_MARKETPLACE.sell1155(
          sellOrder.vaultId,
          sellOrder.idsIn,
          sellOrder.amounts,
          sellOrder.executeCallData,
          payable(address(this)),
          sellOrder.deductRoyalty
        )
      {
        // Pay fees
        uint256 feesLength = fees.length;
        for (uint256 i; i < feesLength; ) {
          Fee memory fee = fees[i];
          _sendETH(fee.recipient, fee.amount);
          unchecked {
            ++i;
          }
        }

        // Forward any left payment to the specified receiver
        _sendAllETH(receiver);
      } catch {
        // Revert if specified
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      // Refund any ERC1155 leftover
      uint256 length = sellOrder.idsIn.length;
      for (uint256 i = 0; i < length; ) {
        _sendAllERC1155(receiver, IERC1155(collection), sellOrder.idsIn[i]);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }
}

File 95 of 129 : NFTXZeroExModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";

import {INFTXMarketplace0xZap} from "../../../interfaces/INFTXMarketplace0xZap.sol";
import {INFTXVault} from "../../../interfaces/INFTXVault.sol";
import {INFTXVaultFactory} from "../../../interfaces/INFTXVaultFactory.sol";

contract NFTXZeroExModule is BaseExchangeModule {
  // --- Fields ---

  INFTXMarketplace0xZap public immutable NFTX_ZEROEX_MARKETPLACE;

  bytes4 public constant ERC721_INTERFACE = 0x80ac58cd;
  bytes4 public constant ERC1155_INTERFACE = 0xd9b67a26;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address nftxMarketplace
  ) BaseModule(owner) BaseExchangeModule(router) {
    NFTX_ZEROEX_MARKETPLACE = INFTXMarketplace0xZap(nftxMarketplace);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Multiple ETH listings ---

  function buyWithETH(
    INFTXMarketplace0xZap.BuyOrder[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = orders.length;
    for (uint256 i = 0; i < length; ) {
      INFTXMarketplace0xZap.BuyOrder memory order = orders[i];

      // Execute fill
      _buy(orders[i], params.fillTo, params.revertIfIncomplete, order.price);

      unchecked {
        ++i;
      }
    }
  }

  // --- Internal ---

  function _buy(
    INFTXMarketplace0xZap.BuyOrder calldata buyOrder,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute the fill
    try
      NFTX_ZEROEX_MARKETPLACE.buyAndRedeem{value: value}(
        buyOrder.vaultId,
        buyOrder.amount,
        buyOrder.specificIds,
        buyOrder.swapCallData,
        payable(receiver)
      )
    {} catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  // --- Single ERC721 offer ---

  function sell(
    INFTXMarketplace0xZap.SellOrder[] calldata orders,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    uint256 length = orders.length;
    for (uint256 i = 0; i < length; ) {
      // Execute fill
      _sell(orders[i], params.fillTo, params.revertIfIncomplete, fees);

      unchecked {
        ++i;
      }
    }
  }

  function _sell(
    INFTXMarketplace0xZap.SellOrder calldata sellOrder,
    address receiver,
    bool revertIfIncomplete,
    Fee[] calldata fees
  ) internal {
    address collection = sellOrder.collection;

    INFTXVault vault = INFTXVault(NFTX_ZEROEX_MARKETPLACE.nftxFactory().vault(sellOrder.vaultId));

    // Execute the sell
    if (!vault.is1155()) {
      _approveERC721IfNeeded(IERC721(collection), address(NFTX_ZEROEX_MARKETPLACE));

      // Return ETH
      try
        NFTX_ZEROEX_MARKETPLACE.mintAndSell721(
          sellOrder.vaultId,
          sellOrder.specificIds,
          sellOrder.swapCallData,
          payable(address(this))
        )
      {
        // Pay fees
        uint256 feesLength = fees.length;
        for (uint256 i; i < feesLength; ) {
          Fee memory fee = fees[i];
          _sendETH(fee.recipient, fee.amount);

          unchecked {
            ++i;
          }
        }

        // Forward any left payment to the specified receiver
        _sendAllETH(receiver);
      } catch {
        // Revert if specified
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      // Refund any ERC721 leftover
      uint256 length = sellOrder.specificIds.length;
      for (uint256 i = 0; i < length; ) {
        _sendAllERC721(receiver, IERC721(collection), sellOrder.specificIds[i]);

        unchecked {
          ++i;
        }
      }
    } else {
      _approveERC1155IfNeeded(IERC1155(collection), address(NFTX_ZEROEX_MARKETPLACE));

      try
        NFTX_ZEROEX_MARKETPLACE.mintAndSell1155(
          sellOrder.vaultId,
          sellOrder.specificIds,
          sellOrder.amounts,
          sellOrder.swapCallData,
          payable(address(this))
        )
      {
        // Pay fees
        uint256 feesLength = fees.length;
        for (uint256 i; i < feesLength; ) {
          Fee memory fee = fees[i];
          _sendETH(fee.recipient, fee.amount);
          unchecked {
            ++i;
          }
        }

        // Forward any left payment to the specified receiver
        _sendAllETH(receiver);
      } catch {
        // Revert if specified
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      // Refund any ERC1155 leftover
      uint256 length = sellOrder.specificIds.length;
      for (uint256 i = 0; i < length; ) {
        _sendAllERC1155(receiver, IERC1155(collection), sellOrder.specificIds[i]);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }
}

File 96 of 129 : OneInchSwapModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";

import {IWETH} from "../../../interfaces/IWETH.sol";

// Notes:
// - supports swapping ETH and ERC20 to any token via a direct path

contract OneInchSwapModule is BaseExchangeModule {
  struct TransferDetail {
    address recipient;
    uint256 amount;
    bool toETH;
  }

  struct SwapDetail {
    IERC20 tokenIn;
    IERC20 tokenOut;
    uint256 amountOut;
    uint256 amountInMaximum;
    bytes data;
  }

  struct Swap {
    SwapDetail params;
    TransferDetail[] transfers;
  }

  // --- Fields ---

  IWETH public immutable WETH;
  address public immutable AGGREGATION_ROUTER;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address weth,
    address aggregationRouter
  ) BaseModule(owner) BaseExchangeModule(router) {
    WETH = IWETH(weth);
    AGGREGATION_ROUTER = aggregationRouter;
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Wrap ---

  function wrap(TransferDetail[] calldata targets) external payable nonReentrant {
    WETH.deposit{value: msg.value}();

    uint256 length = targets.length;
    for (uint256 i = 0; i < length; ) {
      _sendERC20(targets[i].recipient, targets[i].amount, WETH);

      unchecked {
        ++i;
      }
    }
  }

  // --- Unwrap ---

  function unwrap(TransferDetail[] calldata targets) external nonReentrant {
    uint256 balance = WETH.balanceOf(address(this));
    WETH.withdraw(balance);

    uint256 length = targets.length;
    for (uint256 i = 0; i < length; ) {
      _sendETH(targets[i].recipient, targets[i].amount);

      unchecked {
        ++i;
      }
    }
  }

  // --- Swaps ---

  function ethToExactOutput(
    // Assumes all swaps have the same token in
    Swap[] calldata swaps,
    address refundTo,
    bool revertIfIncomplete
  ) external payable nonReentrant refundETHLeftover(refundTo) {
    uint256 swapsLength = swaps.length;
    for (uint256 i; i < swapsLength; ) {
      Swap calldata swap = swaps[i];

      // Execute the swap
      (bool success, ) = AGGREGATION_ROUTER.call{value: swap.params.amountInMaximum}(
        swap.params.data
      );
      if (success) {
        uint256 length = swap.transfers.length;
        for (uint256 j = 0; j < length; ) {
          TransferDetail calldata transferDetail = swap.transfers[j];
          if (transferDetail.toETH) {
            WETH.withdraw(transferDetail.amount);
            _sendETH(transferDetail.recipient, transferDetail.amount);
          } else {
            _sendERC20(
              transferDetail.recipient,
              transferDetail.amount,
              IERC20(swap.params.tokenOut)
            );
          }

          unchecked {
            ++j;
          }
        }
      } else {
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  function erc20ToExactOutput(
    // Assumes all swaps have the same token in
    Swap[] calldata swaps,
    address refundTo,
    bool revertIfIncomplete
  ) external nonReentrant refundERC20Leftover(refundTo, swaps[0].params.tokenIn) {
    uint256 swapsLength = swaps.length;
    for (uint256 i; i < swapsLength; ) {
      Swap calldata swap = swaps[i];

      // Approve the router if needed
      _approveERC20IfNeeded(swap.params.tokenIn, AGGREGATION_ROUTER, swap.params.amountInMaximum);

      // Execute the swap
      (bool success, ) = AGGREGATION_ROUTER.call(swap.params.data);
      if (success) {
        uint256 transfersLength = swap.transfers.length;
        for (uint256 j = 0; j < transfersLength; ) {
          TransferDetail calldata transferDetail = swap.transfers[j];
          if (transferDetail.toETH) {
            WETH.withdraw(transferDetail.amount);
            _sendETH(transferDetail.recipient, transferDetail.amount);
          } else {
            _sendERC20(
              transferDetail.recipient,
              transferDetail.amount,
              IERC20(swap.params.tokenOut)
            );
          }

          unchecked {
            ++j;
          }
        }
      } else {
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }
}

File 97 of 129 : PaymentProcessorModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";

import {IPaymentProcessor} from "../../../interfaces/IPaymentProcessor.sol";

// Notes:
// - supports filling listings (both ETH and ERC20)
// - supports filling offers

contract PaymentProcessorModule is BaseExchangeModule {
  // --- Fields ---

  IPaymentProcessor public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = IPaymentProcessor(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListings(
    IPaymentProcessor.MatchedOrder[] memory saleDetails,
    IPaymentProcessor.SignatureECDSA[] memory signedListings,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = saleDetails.length;
    for (uint256 i; i < length; ) {
      // Execute the fill
      try
        EXCHANGE.buySingleListing{value: saleDetails[i].offerPrice}(
          saleDetails[i],
          signedListings[i],
          IPaymentProcessor.SignatureECDSA({v: 0, r: bytes32(0), s: bytes32(0)})
        )
      {
        // Forward any token to the specified receiver
        if (saleDetails[i].protocol == IPaymentProcessor.TokenProtocols.ERC721) {
          IERC721(saleDetails[i].tokenAddress).safeTransferFrom(
            address(this),
            params.fillTo,
            saleDetails[i].tokenId
          );
        } else {
          IERC1155(saleDetails[i].tokenAddress).safeTransferFrom(
            address(this),
            params.fillTo,
            saleDetails[i].tokenId,
            saleDetails[i].amount,
            ""
          );
        }
      } catch {
        // Revert if specified
        if (params.revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  function acceptERC20Listings(
    IPaymentProcessor.MatchedOrder[] memory saleDetails,
    IPaymentProcessor.SignatureECDSA[] memory signedListings,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    uint256 length = saleDetails.length;
    for (uint256 i; i < length; ) {
      // Execute the fill
      try
        EXCHANGE.buySingleListing(
          saleDetails[i],
          signedListings[i],
          IPaymentProcessor.SignatureECDSA({v: 0, r: bytes32(0), s: bytes32(0)})
        )
      {
        // Forward any token to the specified receiver
        if (saleDetails[i].protocol == IPaymentProcessor.TokenProtocols.ERC721) {
          IERC721(saleDetails[i].tokenAddress).safeTransferFrom(
            address(this),
            params.fillTo,
            saleDetails[i].tokenId
          );
        } else {
          IERC1155(saleDetails[i].tokenAddress).safeTransferFrom(
            address(this),
            params.fillTo,
            saleDetails[i].tokenId,
            saleDetails[i].amount,
            ""
          );
        }
      } catch {
        // Revert if specified
        if (params.revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  function acceptOffers(
    IPaymentProcessor.MatchedOrder[] memory saleDetails,
    IPaymentProcessor.SignatureECDSA[] memory signedOffers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    uint256 length = saleDetails.length;
    for (uint256 i; i < length; ) {
      // Approve the exchange if needed
      if (saleDetails[i].protocol == IPaymentProcessor.TokenProtocols.ERC721) {
        _approveERC721IfNeeded(IERC721(saleDetails[i].tokenAddress), address(EXCHANGE));
      } else {
        _approveERC1155IfNeeded(IERC1155(saleDetails[i].tokenAddress), address(EXCHANGE));
      }

      // Execute the fill
      try
        EXCHANGE.buySingleListing(
          saleDetails[i],
          IPaymentProcessor.SignatureECDSA({v: 0, r: bytes32(0), s: bytes32(0)}),
          signedOffers[i]
        )
      {
        // Pay fees
        uint256 feesLength = fees.length;
        for (uint256 j; j < feesLength; ) {
          Fee memory fee = fees[j];
          _sendERC20(fee.recipient, fee.amount, IERC20(saleDetails[i].paymentCoin));

          unchecked {
            ++j;
          }
        }

        // Forward any left payment to the specified receiver
        _sendAllERC20(params.fillTo, IERC20(saleDetails[i].paymentCoin));
      } catch {
        // Revert if specified
        if (params.revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  // --- ERC1271 ---

  function isValidSignature(bytes32, bytes memory) external pure returns (bytes4) {
    return this.isValidSignature.selector;
  }

  // --- ERC721 / ERC1155 hooks ---

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }
}

File 98 of 129 : RaribleModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {IRarible} from "../../../interfaces/IRarible.sol";

// Notes:
// - supports filling listings (both ERC721/ERC1155 but only ETH-denominated)
// - supports filling offers (both ERC721/ERC1155)

contract RaribleModule is BaseExchangeModule {
  using SafeERC20 for IERC20;

  // --- Fields ---

  IRarible public immutable EXCHANGE;

  address public immutable TRANSFER_MANAGER;

  bytes4 public constant ERC721_INTERFACE = 0x80ac58cd;
  bytes4 public constant ERC1155_INTERFACE = 0xd9b67a26;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange,
    address transferManager
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = IRarible(exchange);
    TRANSFER_MANAGER = transferManager;
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    IRarible.Order calldata orderLeft,
    bytes calldata signatureLeft,
    IRarible.Order calldata orderRight,
    bytes calldata signatureRight,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buy(
      orderLeft,
      signatureLeft,
      orderRight,
      signatureRight,
      params.fillTo,
      params.revertIfIncomplete,
      params.amount
    );
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    IRarible.Order[] calldata ordersLeft,
    bytes[] calldata signaturesLeft,
    IRarible.Order[] calldata ordersRight,
    bytes calldata signatureRight,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    for (uint256 i = 0; i < ordersLeft.length; ) {
      IRarible.Order calldata orderLeft = ordersLeft[i];
      IRarible.Order calldata orderRight = ordersRight[i];

      // Execute fill
      _buy(
        orderLeft,
        signaturesLeft[i],
        orderRight,
        signatureRight,
        params.fillTo,
        params.revertIfIncomplete,
        orderLeft.takeAsset.value
      );

      unchecked {
        ++i;
      }
    }
  }

  // --- [ERC721] Single offer ---

  function acceptERC721Offer(
    IRarible.Order calldata orderLeft,
    bytes calldata signatureLeft,
    IRarible.Order calldata orderRight,
    bytes calldata signatureRight,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    (address token, uint256 tokenId) = abi.decode(
      orderRight.makeAsset.assetType.data,
      (address, uint256)
    );

    IERC721 collection = IERC721(address(token));

    // Approve the transfer manager if needed
    _approveERC721IfNeeded(collection, TRANSFER_MANAGER);

    // Execute the fill
    _sell(
      orderLeft,
      signatureLeft,
      orderRight,
      signatureRight,
      params.fillTo,
      params.revertIfIncomplete,
      fees
    );

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, collection, tokenId);
  }

  // --- [ERC1155] Single offer ---

  function acceptERC1155Offer(
    IRarible.Order calldata orderLeft,
    bytes calldata signatureLeft,
    IRarible.Order calldata orderRight,
    bytes calldata signatureRight,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    (address token, uint256 tokenId) = abi.decode(
      orderRight.makeAsset.assetType.data,
      (address, uint256)
    );

    IERC1155 collection = IERC1155(address(token));

    // Approve the transfer manager if needed
    _approveERC1155IfNeeded(collection, TRANSFER_MANAGER);

    // Execute the fill
    _sell(
      orderLeft,
      signatureLeft,
      orderRight,
      signatureRight,
      params.fillTo,
      params.revertIfIncomplete,
      fees
    );

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, collection, tokenId);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  function _buy(
    IRarible.Order calldata orderLeft,
    bytes calldata signatureLeft,
    IRarible.Order calldata orderRight,
    bytes calldata signatureRight,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute the fill
    try EXCHANGE.matchOrders{value: value}(orderLeft, signatureLeft, orderRight, signatureRight) {
      (address token, uint256 tokenId) = abi.decode(
        orderLeft.makeAsset.assetType.data,
        (address, uint256)
      );
      IERC165 collection = IERC165(token);

      // Forward any token to the specified receiver
      bool isERC721 = collection.supportsInterface(ERC721_INTERFACE);
      if (isERC721) {
        IERC721(address(collection)).safeTransferFrom(address(this), receiver, tokenId);
      } else {
        IERC1155(address(collection)).safeTransferFrom(
          address(this),
          receiver,
          tokenId,
          orderRight.takeAsset.value,
          ""
        );
      }
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _sell(
    IRarible.Order calldata orderLeft,
    bytes calldata signatureLeft,
    IRarible.Order calldata orderRight,
    bytes calldata signatureRight,
    address receiver,
    bool revertIfIncomplete,
    Fee[] calldata fees
  ) internal {
    // Execute the fill
    try EXCHANGE.matchOrders(orderLeft, signatureLeft, orderRight, signatureRight) {
      // Pay fees
      address token = abi.decode(orderLeft.makeAsset.assetType.data, (address));
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];

        _sendERC20(fee.recipient, fee.amount, IERC20(token));

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(receiver, IERC20(token));
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 99 of 129 : SeaportModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ISeaport} from "../../../interfaces/ISeaport.sol";

// Notes on the Seaport module:
// - supports filling listings (both ERC721/ERC1155)
// - supports filling offers (both ERC721/ERC1155)

contract SeaportModule is BaseExchangeModule {
  // --- Structs ---

  struct SeaportETHListingWithPrice {
    ISeaport.AdvancedOrder order;
    uint256 price;
  }

  // --- Fields ---

  ISeaport public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = ISeaport(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    ISeaport.AdvancedOrder calldata order,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(
        order,
        new ISeaport.CriteriaResolver[](0),
        params.fillTo,
        params.amount
      )
      : _fillSingleOrder(order, new ISeaport.CriteriaResolver[](0), params.fillTo, params.amount);
  }

  // --- Single ERC20 listing ---

  function acceptERC20Listing(
    ISeaport.AdvancedOrder calldata order,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(
        order,
        new ISeaport.CriteriaResolver[](0),
        params.fillTo,
        0
      )
      : _fillSingleOrder(order, new ISeaport.CriteriaResolver[](0), params.fillTo, 0);
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    SeaportETHListingWithPrice[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillSingleOrderWithRevertIfIncomplete(
          orders[i].order,
          criteriaResolvers,
          params.fillTo,
          orders[i].price
        );

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillSingleOrder(orders[i].order, criteriaResolvers, params.fillTo, orders[i].price);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Multiple ERC20 listings ---

  function acceptERC20Listings(
    ISeaport.AdvancedOrder[] calldata orders,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillSingleOrderWithRevertIfIncomplete(orders[i], criteriaResolvers, params.fillTo, 0);

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillSingleOrder(orders[i], criteriaResolvers, params.fillTo, 0);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Single ERC721 offer ---

  function acceptERC721Offer(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    // Extract the ERC721 token from the consideration items
    ISeaport.ConsiderationItem calldata nftItem = order.parameters.consideration[0];
    if (
      nftItem.itemType != ISeaport.ItemType.ERC721 &&
      nftItem.itemType != ISeaport.ItemType.ERC721_WITH_CRITERIA
    ) {
      revert WrongParams();
    }
    IERC721 nftToken = IERC721(nftItem.token);

    // Extract the payment token from the offer items
    ISeaport.OfferItem calldata paymentItem = order.parameters.offer[0];
    IERC20 paymentToken = IERC20(paymentItem.token);

    // Approve the exchange if needed
    _approveERC721IfNeeded(nftToken, address(EXCHANGE));
    _approveERC20IfNeeded(paymentToken, address(EXCHANGE), type(uint256).max);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(order, criteriaResolvers, address(this), 0)
      : _fillSingleOrder(order, criteriaResolvers, address(this), 0);

    uint256 identifier = nftItem.itemType == ISeaport.ItemType.ERC721
      ? nftItem.identifierOrCriteria
      : criteriaResolvers[0].identifier;

    // Pay fees
    if (nftToken.ownerOf(identifier) != address(this)) {
      // Only pay fees if the fill was successful
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, paymentToken);

        unchecked {
          ++i;
        }
      }
    }

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, nftToken, identifier);

    // Forward any left payment to the specified receiver
    _sendAllERC20(params.fillTo, paymentToken);
  }

  // --- Single ERC1155 offer ---

  function acceptERC1155Offer(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    // Extract the ERC1155 token from the consideration items
    ISeaport.ConsiderationItem calldata nftItem = order.parameters.consideration[0];
    if (
      nftItem.itemType != ISeaport.ItemType.ERC1155 &&
      nftItem.itemType != ISeaport.ItemType.ERC1155_WITH_CRITERIA
    ) {
      revert WrongParams();
    }
    IERC1155 nftToken = IERC1155(nftItem.token);

    // Extract the payment token from the offer items
    ISeaport.OfferItem calldata paymentItem = order.parameters.offer[0];
    IERC20 paymentToken = IERC20(paymentItem.token);

    // Approve the exchange if needed
    _approveERC1155IfNeeded(nftToken, address(EXCHANGE));
    _approveERC20IfNeeded(paymentToken, address(EXCHANGE), type(uint256).max);

    uint256 identifier = nftItem.itemType == ISeaport.ItemType.ERC1155
      ? nftItem.identifierOrCriteria
      : criteriaResolvers[0].identifier;

    uint256 balanceBefore = nftToken.balanceOf(address(this), identifier);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(order, criteriaResolvers, address(this), 0)
      : _fillSingleOrder(order, criteriaResolvers, address(this), 0);

    uint256 balanceAfter = nftToken.balanceOf(address(this), identifier);

    // Pay fees
    uint256 amountFilled = balanceBefore - balanceAfter;
    if (amountFilled > 0) {
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(
          fee.recipient,
          // Only pay fees for the amount that was actually filled
          (fee.amount * amountFilled) / order.numerator,
          paymentToken
        );

        unchecked {
          ++i;
        }
      }
    }

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, nftToken, identifier);

    // Forward any left payment to the specified receiver
    _sendAllERC20(params.fillTo, paymentToken);
  }

  // --- Generic handler (used for Seaport-based approvals) ---

  function matchOrders(
    ISeaport.Order[] calldata orders,
    ISeaport.Fulfillment[] calldata fulfillments
  ) external nonReentrant {
    // We don't perform any kind of input or return value validation,
    // so this function should be used with precaution - the official
    // way to use it is only for Seaport-based approvals
    EXCHANGE.matchOrders(orders, fulfillments);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  // NOTE: In lots of cases, Seaport will not revert if fills were not
  // fully executed. An example of that is partial filling, which will
  // successfully fill any amount that is still available (including a
  // zero amount). One way to ensure that we revert in case of partial
  // executions is to check the order's filled amount before and after
  // we trigger the fill (we can use Seaport's `getOrderStatus` method
  // to check). Since this can be expensive in terms of gas, we have a
  // separate method variant to be called when reverts are enabled.

  function _fillSingleOrder(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    address receiver,
    uint256 value
  ) internal {
    // Execute the fill
    try
      EXCHANGE.fulfillAdvancedOrder{value: value}(order, criteriaResolvers, bytes32(0), receiver)
    {} catch {}
  }

  function _fillSingleOrderWithRevertIfIncomplete(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    address receiver,
    uint256 value
  ) internal {
    // Cache the order's hash
    bytes32 orderHash = _getOrderHash(order.parameters);

    // Before filling, get the order's filled amount
    uint256 beforeFilledAmount = _getFilledAmount(orderHash);

    // Execute the fill
    bool success;
    try
      EXCHANGE.fulfillAdvancedOrder{value: value}(order, criteriaResolvers, bytes32(0), receiver)
    returns (bool fulfilled) {
      success = fulfilled;
    } catch {
      revert UnsuccessfulFill();
    }

    if (!success) {
      revert UnsuccessfulFill();
    } else {
      // After successfully filling, get the order's filled amount
      uint256 afterFilledAmount = _getFilledAmount(orderHash);

      // Make sure the amount filled as part of this call is correct
      if (afterFilledAmount - beforeFilledAmount != order.numerator) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _getOrderHash(
    // Must use `memory` instead of `calldata` for the below cast
    ISeaport.OrderParameters memory orderParameters
  ) internal view returns (bytes32 orderHash) {
    // `OrderParameters` and `OrderComponents` share the exact same
    // fields, apart from the last one, so here we simply treat the
    // `orderParameters` argument as `OrderComponents` and then set
    // the last field to the correct data
    ISeaport.OrderComponents memory orderComponents;
    assembly {
      orderComponents := orderParameters
    }
    orderComponents.counter = EXCHANGE.getCounter(orderParameters.offerer);

    orderHash = EXCHANGE.getOrderHash(orderComponents);
  }

  function _getFilledAmount(bytes32 orderHash) internal view returns (uint256 totalFilled) {
    (, , totalFilled, ) = EXCHANGE.getOrderStatus(orderHash);
  }
}

File 100 of 129 : SeaportV14Module.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ISeaport} from "../../../interfaces/ISeaport.sol";

// Notes on the Seaport module:
// - supports filling listings (both ERC721/ERC1155)
// - supports filling offers (both ERC721/ERC1155)

contract SeaportV14Module is BaseExchangeModule {
  // --- Structs ---

  struct SeaportETHListingWithPrice {
    ISeaport.AdvancedOrder order;
    uint256 price;
  }

  // --- Fields ---

  ISeaport public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = ISeaport(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    ISeaport.AdvancedOrder calldata order,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(
        order,
        new ISeaport.CriteriaResolver[](0),
        params.fillTo,
        params.amount
      )
      : _fillSingleOrder(order, new ISeaport.CriteriaResolver[](0), params.fillTo, params.amount);
  }

  // --- Single ERC20 listing ---

  function acceptERC20Listing(
    ISeaport.AdvancedOrder calldata order,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(
        order,
        new ISeaport.CriteriaResolver[](0),
        params.fillTo,
        0
      )
      : _fillSingleOrder(order, new ISeaport.CriteriaResolver[](0), params.fillTo, 0);
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    SeaportETHListingWithPrice[] calldata orders,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillSingleOrderWithRevertIfIncomplete(
          orders[i].order,
          criteriaResolvers,
          params.fillTo,
          orders[i].price
        );

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillSingleOrder(orders[i].order, criteriaResolvers, params.fillTo, orders[i].price);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Multiple ERC20 listings ---

  function acceptERC20Listings(
    ISeaport.AdvancedOrder[] calldata orders,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    uint256 length = orders.length;
    ISeaport.CriteriaResolver[] memory criteriaResolvers = new ISeaport.CriteriaResolver[](0);

    // Execute the fills
    if (params.revertIfIncomplete) {
      for (uint256 i; i < length; ) {
        _fillSingleOrderWithRevertIfIncomplete(orders[i], criteriaResolvers, params.fillTo, 0);

        unchecked {
          ++i;
        }
      }
    } else {
      for (uint256 i; i < length; ) {
        _fillSingleOrder(orders[i], criteriaResolvers, params.fillTo, 0);

        unchecked {
          ++i;
        }
      }
    }
  }

  // --- Single ERC721 offer ---

  function acceptERC721Offer(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    // Extract the ERC721 token from the consideration items
    ISeaport.ConsiderationItem calldata nftItem = order.parameters.consideration[0];
    if (
      nftItem.itemType != ISeaport.ItemType.ERC721 &&
      nftItem.itemType != ISeaport.ItemType.ERC721_WITH_CRITERIA
    ) {
      revert WrongParams();
    }
    IERC721 nftToken = IERC721(nftItem.token);

    // Extract the payment token from the offer items
    ISeaport.OfferItem calldata paymentItem = order.parameters.offer[0];
    IERC20 paymentToken = IERC20(paymentItem.token);

    // Approve the exchange if needed
    _approveERC721IfNeeded(nftToken, address(EXCHANGE));
    _approveERC20IfNeeded(paymentToken, address(EXCHANGE), type(uint256).max);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(order, criteriaResolvers, address(this), 0)
      : _fillSingleOrder(order, criteriaResolvers, address(this), 0);

    uint256 identifier = nftItem.itemType == ISeaport.ItemType.ERC721
      ? nftItem.identifierOrCriteria
      : criteriaResolvers[0].identifier;

    // Pay fees
    if (nftToken.ownerOf(identifier) != address(this)) {
      // Only pay fees if the fill was successful
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, paymentToken);

        unchecked {
          ++i;
        }
      }
    }

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, nftToken, identifier);

    // Forward any left payment to the specified receiver
    _sendAllERC20(params.fillTo, paymentToken);
  }

  // --- Single ERC1155 offer ---

  function acceptERC1155Offer(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    // Extract the ERC1155 token from the consideration items
    ISeaport.ConsiderationItem calldata nftItem = order.parameters.consideration[0];
    if (
      nftItem.itemType != ISeaport.ItemType.ERC1155 &&
      nftItem.itemType != ISeaport.ItemType.ERC1155_WITH_CRITERIA
    ) {
      revert WrongParams();
    }
    IERC1155 nftToken = IERC1155(nftItem.token);

    // Extract the payment token from the offer items
    ISeaport.OfferItem calldata paymentItem = order.parameters.offer[0];
    IERC20 paymentToken = IERC20(paymentItem.token);

    // Approve the exchange if needed
    _approveERC1155IfNeeded(nftToken, address(EXCHANGE));
    _approveERC20IfNeeded(paymentToken, address(EXCHANGE), type(uint256).max);

    uint256 identifier = nftItem.itemType == ISeaport.ItemType.ERC1155
      ? nftItem.identifierOrCriteria
      : criteriaResolvers[0].identifier;

    uint256 balanceBefore = nftToken.balanceOf(address(this), identifier);

    // Execute the fill
    params.revertIfIncomplete
      ? _fillSingleOrderWithRevertIfIncomplete(order, criteriaResolvers, address(this), 0)
      : _fillSingleOrder(order, criteriaResolvers, address(this), 0);

    uint256 balanceAfter = nftToken.balanceOf(address(this), identifier);

    // Pay fees
    uint256 amountFilled = balanceBefore - balanceAfter;
    if (amountFilled > 0) {
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(
          fee.recipient,
          // Only pay fees for the amount that was actually filled
          (fee.amount * amountFilled) / order.numerator,
          paymentToken
        );

        unchecked {
          ++i;
        }
      }
    }

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, nftToken, identifier);

    // Forward any left payment to the specified receiver
    _sendAllERC20(params.fillTo, paymentToken);
  }

  // --- Generic handler (used for Seaport-based approvals) ---

  function matchOrders(
    ISeaport.Order[] calldata orders,
    ISeaport.Fulfillment[] calldata fulfillments
  ) external nonReentrant {
    // We don't perform any kind of input or return value validation,
    // so this function should be used with precaution - the official
    // way to use it is only for Seaport-based approvals
    EXCHANGE.matchOrders(orders, fulfillments);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  // NOTE: In lots of cases, Seaport will not revert if fills were not
  // fully executed. An example of that is partial filling, which will
  // successfully fill any amount that is still available (including a
  // zero amount). One way to ensure that we revert in case of partial
  // executions is to check the order's filled amount before and after
  // we trigger the fill (we can use Seaport's `getOrderStatus` method
  // to check). Since this can be expensive in terms of gas, we have a
  // separate method variant to be called when reverts are enabled.

  function _fillSingleOrder(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    address receiver,
    uint256 value
  ) internal {
    // Execute the fill
    try
      EXCHANGE.fulfillAdvancedOrder{value: value}(order, criteriaResolvers, bytes32(0), receiver)
    {} catch {}
  }

  function _fillSingleOrderWithRevertIfIncomplete(
    ISeaport.AdvancedOrder calldata order,
    // Use `memory` instead of `calldata` to avoid `Stack too deep` errors
    ISeaport.CriteriaResolver[] memory criteriaResolvers,
    address receiver,
    uint256 value
  ) internal {
    // Cache the order's hash
    bytes32 orderHash = _getOrderHash(order.parameters);

    // Before filling, get the order's filled amount
    uint256 beforeFilledAmount = _getFilledAmount(orderHash);

    // Execute the fill
    bool success;
    try
      EXCHANGE.fulfillAdvancedOrder{value: value}(order, criteriaResolvers, bytes32(0), receiver)
    returns (bool fulfilled) {
      success = fulfilled;
    } catch {
      revert UnsuccessfulFill();
    }

    if (!success) {
      revert UnsuccessfulFill();
    } else {
      // After successfully filling, get the order's filled amount
      uint256 afterFilledAmount = _getFilledAmount(orderHash);

      // Make sure the amount filled as part of this call is correct
      if (afterFilledAmount - beforeFilledAmount != order.numerator) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _getOrderHash(
    // Must use `memory` instead of `calldata` for the below cast
    ISeaport.OrderParameters memory orderParameters
  ) internal view returns (bytes32 orderHash) {
    // `OrderParameters` and `OrderComponents` share the exact same
    // fields, apart from the last one, so here we simply treat the
    // `orderParameters` argument as `OrderComponents` and then set
    // the last field to the correct data
    ISeaport.OrderComponents memory orderComponents;
    assembly {
      orderComponents := orderParameters
    }
    orderComponents.counter = EXCHANGE.getCounter(orderParameters.offerer);

    orderHash = EXCHANGE.getOrderHash(orderComponents);
  }

  function _getFilledAmount(bytes32 orderHash) internal view returns (uint256 totalFilled) {
    (, , totalFilled, ) = EXCHANGE.getOrderStatus(orderHash);
  }
}

File 101 of 129 : SudoswapModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ISudoswapPair, ISudoswapRouter} from "../../../interfaces/ISudoswap.sol";

contract SudoswapModule is BaseExchangeModule {
  // --- Fields ---

  ISudoswapRouter public immutable SUDOSWAP_ROUTER;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address sudoswapRouter
  ) BaseModule(owner) BaseExchangeModule(router) {
    SUDOSWAP_ROUTER = ISudoswapRouter(sudoswapRouter);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Multiple ETH listings ---

  function buyWithETH(
    ISudoswapPair[] calldata pairs,
    uint256[] calldata nftIds,
    uint256 deadline,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 pairsLength = pairs.length;
    for (uint256 i; i < pairsLength; ) {
      // Build router data
      ISudoswapRouter.PairSwapSpecific[] memory swapList = new ISudoswapRouter.PairSwapSpecific[](
        1
      );
      swapList[0] = ISudoswapRouter.PairSwapSpecific({pair: pairs[i], nftIds: new uint256[](1)});
      swapList[0].nftIds[0] = nftIds[i];

      // Fetch the current price quote
      (, , , uint256 price, ) = pairs[i].getBuyNFTQuote(1);

      // Execute fill
      try
        SUDOSWAP_ROUTER.swapETHForSpecificNFTs{value: price}(
          swapList,
          address(this),
          params.fillTo,
          deadline
        )
      {} catch {
        if (params.revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  // --- Multiple ERC20 listings ---

  function buyWithERC20(
    ISudoswapPair[] calldata pairs,
    uint256[] calldata nftIds,
    uint256 deadline,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the router if needed
    _approveERC20IfNeeded(params.token, address(SUDOSWAP_ROUTER), params.amount);

    uint256 pairsLength = pairs.length;
    for (uint256 i; i < pairsLength; ) {
      // Build router data
      ISudoswapRouter.PairSwapSpecific[] memory swapList = new ISudoswapRouter.PairSwapSpecific[](
        1
      );
      swapList[0] = ISudoswapRouter.PairSwapSpecific({pair: pairs[i], nftIds: new uint256[](1)});
      swapList[0].nftIds[0] = nftIds[i];

      // Fetch the current price quote
      (, , , uint256 price, ) = pairs[i].getBuyNFTQuote(1);

      // Execute fill
      try
        SUDOSWAP_ROUTER.swapERC20ForSpecificNFTs(swapList, price, params.fillTo, deadline)
      {} catch {
        if (params.revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  // --- Single ERC721 offer ---

  function sell(
    ISudoswapPair pair,
    uint256 nftId,
    uint256 minOutput,
    uint256 deadline,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    IERC721 collection = pair.nft();

    // Approve the router if needed
    _approveERC721IfNeeded(collection, address(SUDOSWAP_ROUTER));

    // Build router data
    ISudoswapRouter.PairSwapSpecific[] memory swapList = new ISudoswapRouter.PairSwapSpecific[](1);
    swapList[0] = ISudoswapRouter.PairSwapSpecific({pair: pair, nftIds: new uint256[](1)});
    swapList[0].nftIds[0] = nftId;

    // Execute fill
    try SUDOSWAP_ROUTER.swapNFTsForToken(swapList, minOutput, address(this), deadline) {
      ISudoswapPair.PairVariant variant = pair.pairVariant();

      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        uint8(variant) < 2
          ? _sendETH(fee.recipient, fee.amount)
          : _sendERC20(fee.recipient, fee.amount, pair.token());

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      uint8(variant) < 2 ? _sendAllETH(params.fillTo) : _sendAllERC20(params.fillTo, pair.token());
    } catch {
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, collection, nftId);
  }

  // --- ERC721 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }
}

File 102 of 129 : SudoswapV2Module.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ISudoswapPairV2} from "../../../interfaces/ISudoswapV2.sol";

contract SudoswapV2Module is BaseExchangeModule {
  // --- Constructor ---

  constructor(address owner, address router) BaseModule(owner) BaseExchangeModule(router) {}

  // --- Fallback ---

  receive() external payable {}

  // --- Multiple ETH listings ---

  function buyWithETH(
    ISudoswapPairV2[] calldata pairs,
    // Token ids for ERC721 pairs, amounts for ERC1155 pairs
    uint256[] calldata nftIds,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256[] memory tokenIds = new uint256[](1);

    uint256 pairsLength = pairs.length;
    for (uint256 i; i < pairsLength; ) {
      ISudoswapPairV2 pair = pairs[i];
      ISudoswapPairV2.PairVariant variant = pair.pairVariant();

      bool isERC1155 = isERC1155Pair(variant);

      // Fetch the current price
      (, , , uint256 price, , ) = pair.getBuyNFTQuote(
        isERC1155 ? pair.nftId() : nftIds[i],
        isERC1155 ? nftIds[i] : 1
      );

      tokenIds[0] = nftIds[i];

      // Execute fill
      try
        pair.swapTokenForSpecificNFTs{value: price}(
          tokenIds,
          price,
          params.fillTo,
          false,
          address(0)
        )
      {} catch {
        if (params.revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  // --- Multiple ERC20 listings ---

  function buyWithERC20(
    ISudoswapPairV2[] calldata pairs,
    // Token ids for ERC721 pairs, amounts for ERC1155 pairs
    uint256[] calldata nftIds,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    uint256[] memory tokenIds = new uint256[](1);

    uint256 pairsLength = pairs.length;
    for (uint256 i; i < pairsLength; ) {
      ISudoswapPairV2 pair = pairs[i];
      ISudoswapPairV2.PairVariant variant = pair.pairVariant();

      bool isERC1155 = isERC1155Pair(variant);

      // Fetch the current price
      (, , , uint256 price, , ) = pair.getBuyNFTQuote(
        isERC1155 ? pair.nftId() : nftIds[i],
        isERC1155 ? nftIds[i] : 1
      );

      tokenIds[0] = nftIds[i];

      // Approve the pair if needed
      _approveERC20IfNeeded(params.token, address(pair), params.amount);

      // Execute fill
      try
        pair.swapTokenForSpecificNFTs(tokenIds, price, params.fillTo, false, address(0))
      {} catch {
        if (params.revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  // --- Single offer ---

  function sell(
    ISudoswapPairV2 pair,
    // Token id for ERC721 pairs, amount for ERC1155 pairs
    uint256 nftId,
    uint256 minOutput,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    ISudoswapPairV2.PairVariant variant = pair.pairVariant();

    bool isETH = isETHPair(variant);
    address nft = pair.nft();

    IERC20 token = isETH ? IERC20(address(0)) : pair.token();

    // Approve the pair if needed
    if (!isERC1155Pair(variant)) {
      _approveERC721IfNeeded(IERC721(nft), address(pair));
    } else {
      _approveERC1155IfNeeded(IERC1155(nft), address(pair));
    }

    // Build router data
    uint256[] memory tokenIds = new uint256[](1);
    tokenIds[0] = nftId;

    // Execute fill
    try pair.swapNFTsForToken(tokenIds, minOutput, payable(address(this)), false, address(0)) {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        isETH ? _sendETH(fee.recipient, fee.amount) : _sendERC20(fee.recipient, fee.amount, token);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      isETH ? _sendAllETH(params.fillTo) : _sendAllERC20(params.fillTo, token);
    } catch {
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any leftovers
    if (!isERC1155Pair(variant)) {
      _sendAllERC721(params.refundTo, IERC721(nft), nftId);
    } else {
      _sendAllERC1155(params.refundTo, IERC1155(nft), pair.nftId());
    }
  }

  // --- ERC721/1155 hooks ---

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal methods ---

  function isERC1155Pair(ISudoswapPairV2.PairVariant vaiant) internal pure returns (bool) {
    return
      ISudoswapPairV2.PairVariant.ERC1155_ERC20 == vaiant ||
      ISudoswapPairV2.PairVariant.ERC1155_ETH == vaiant;
  }

  function isETHPair(ISudoswapPairV2.PairVariant vaiant) internal pure returns (bool) {
    return
      ISudoswapPairV2.PairVariant.ERC721_ETH == vaiant ||
      ISudoswapPairV2.PairVariant.ERC1155_ETH == vaiant;
  }
}

File 103 of 129 : SuperRareModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {ISuperRare} from "../../../interfaces/ISuperRare.sol";

// Notes:
// - only supports filling "buy now" listings (ERC721 and ETH-denominated)

contract SuperRareModule is BaseExchangeModule {
  // --- Structs ---

  struct Listing {
    IERC721 token;
    uint256 tokenId;
    address currency;
    uint256 price;
    uint256 priceWithFees;
  }

  // --- Fields ---

  ISuperRare public immutable BAZAAR;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address bazaar
  ) BaseModule(owner) BaseExchangeModule(router) {
    BAZAAR = ISuperRare(bazaar);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    Listing calldata listing,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buy(
      listing.token,
      listing.tokenId,
      listing.currency,
      listing.price,
      params.fillTo,
      params.revertIfIncomplete,
      listing.priceWithFees
    );
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    Listing[] calldata listings,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    uint256 length = listings.length;
    for (uint256 i = 0; i < length; ) {
      _buy(
        listings[i].token,
        listings[i].tokenId,
        listings[i].currency,
        listings[i].price,
        params.fillTo,
        params.revertIfIncomplete,
        listings[i].priceWithFees
      );

      unchecked {
        ++i;
      }
    }
  }

  // --- ERC721 hooks ---

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata // data
  ) external pure returns (bytes4) {
    return this.onERC721Received.selector;
  }

  // --- Internal ---

  function _buy(
    IERC721 token,
    uint256 tokenId,
    address currency,
    uint256 price,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute fill
    try BAZAAR.buy{value: value}(token, tokenId, currency, price) {
      token.safeTransferFrom(address(this), receiver, tokenId);
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 104 of 129 : SwapModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {IUniswapV3Router} from "../../../interfaces/IUniswapV3Router.sol";
import {IWETH} from "../../../interfaces/IWETH.sol";

// Notes:
// - supports swapping ETH and ERC20 to any token via a direct path

contract SwapModule is BaseExchangeModule {
  struct TransferDetail {
    address recipient;
    uint256 amount;
    bool toETH;
  }

  struct BuySwap {
    IUniswapV3Router.ExactOutputSingleParams params;
    TransferDetail[] transfers;
  }

  struct SellSwap {
    IUniswapV3Router.ExactInputSingleParams params;
    TransferDetail[] transfers;
  }

  // --- Fields ---

  IWETH public immutable WETH;
  IUniswapV3Router public immutable SWAP_ROUTER;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address weth,
    address swapRouter
  ) BaseModule(owner) BaseExchangeModule(router) {
    WETH = IWETH(weth);
    SWAP_ROUTER = IUniswapV3Router(swapRouter);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Wrap ---

  function wrap(TransferDetail[] calldata targets) external payable nonReentrant {
    WETH.deposit{value: msg.value}();

    uint256 length = targets.length;
    for (uint256 i = 0; i < length; ) {
      // Zero represents "everything"
      uint256 amount = targets[i].amount == 0 ? WETH.balanceOf(address(this)) : targets[i].amount;
      _sendERC20(targets[i].recipient, amount, WETH);

      unchecked {
        ++i;
      }
    }
  }

  // --- Unwrap ---

  function unwrap(TransferDetail[] calldata targets) external nonReentrant {
    uint256 balance = WETH.balanceOf(address(this));
    WETH.withdraw(balance);

    uint256 length = targets.length;
    for (uint256 i = 0; i < length; ) {
      // Zero represents "everything"
      uint256 amount = targets[i].amount == 0 ? address(this).balance : targets[i].amount;
      _sendETH(targets[i].recipient, amount);

      unchecked {
        ++i;
      }
    }
  }

  // --- Swaps ---

  function ethToExactOutput(
    // Assumes all swaps have the same token in
    BuySwap[] calldata swaps,
    address refundTo,
    bool revertIfIncomplete
  ) external payable nonReentrant refundETHLeftover(refundTo) {
    uint256 swapsLength = swaps.length;
    for (uint256 i; i < swapsLength; ) {
      BuySwap calldata swap = swaps[i];

      // Execute the swap
      try SWAP_ROUTER.exactOutputSingle{value: swap.params.amountInMaximum}(swap.params) {
        uint256 length = swap.transfers.length;
        for (uint256 j = 0; j < length; ) {
          TransferDetail calldata transferDetail = swap.transfers[j];
          if (transferDetail.toETH) {
            // Zero represents "everything"
            uint256 amount = transferDetail.amount == 0
              ? WETH.balanceOf(address(this))
              : transferDetail.amount;

            WETH.withdraw(amount);
            _sendETH(transferDetail.recipient, amount);
          } else {
            // Zero represents "everything"
            uint256 amount = transferDetail.amount == 0
              ? IERC20(swap.params.tokenOut).balanceOf(address(this))
              : transferDetail.amount;

            _sendERC20(transferDetail.recipient, amount, IERC20(swap.params.tokenOut));
          }

          unchecked {
            ++j;
          }
        }
      } catch {
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }

    // Refund any ETH stucked in the router
    SWAP_ROUTER.refundETH();
  }

  function erc20ToExactOutput(
    // Assumes all swaps have the same token in
    BuySwap[] calldata swaps,
    address refundTo,
    bool revertIfIncomplete
  ) external nonReentrant refundERC20Leftover(refundTo, swaps[0].params.tokenIn) {
    uint256 swapsLength = swaps.length;
    for (uint256 i; i < swapsLength; ) {
      BuySwap calldata swap = swaps[i];

      // Approve the router if needed
      _approveERC20IfNeeded(swap.params.tokenIn, address(SWAP_ROUTER), swap.params.amountInMaximum);

      // Execute the swap
      try SWAP_ROUTER.exactOutputSingle(swap.params) {
        uint256 transfersLength = swap.transfers.length;
        for (uint256 j = 0; j < transfersLength; ) {
          TransferDetail calldata transferDetail = swap.transfers[j];
          if (transferDetail.toETH) {
            // Zero represents "everything"
            uint256 amount = transferDetail.amount == 0
              ? WETH.balanceOf(address(this))
              : transferDetail.amount;

            WETH.withdraw(amount);
            _sendETH(transferDetail.recipient, amount);
          } else {
            // Zero represents "everything"
            uint256 amount = transferDetail.amount == 0
              ? IERC20(swap.params.tokenOut).balanceOf(address(this))
              : transferDetail.amount;

            _sendERC20(transferDetail.recipient, amount, IERC20(swap.params.tokenOut));
          }

          unchecked {
            ++j;
          }
        }
      } catch {
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }

  function erc20ToExactInput(
    // Assumes all swaps have the same token in
    SellSwap[] calldata swaps,
    address refundTo,
    bool revertIfIncomplete
  ) external nonReentrant refundERC20Leftover(refundTo, swaps[0].params.tokenIn) {
    uint256 swapsLength = swaps.length;
    for (uint256 i; i < swapsLength; ) {
      SellSwap calldata swap = swaps[i];

      // Approve the router if needed
      _approveERC20IfNeeded(swap.params.tokenIn, address(SWAP_ROUTER), swap.params.amountIn);

      // Execute the swap
      try SWAP_ROUTER.exactInputSingle(swap.params) {
        uint256 transfersLength = swap.transfers.length;
        for (uint256 j = 0; j < transfersLength; ) {
          TransferDetail calldata transferDetail = swap.transfers[j];
          if (transferDetail.toETH) {
            // Zero represents "everything"
            uint256 amount = transferDetail.amount == 0
              ? WETH.balanceOf(address(this))
              : transferDetail.amount;

            WETH.withdraw(amount);
            _sendETH(transferDetail.recipient, amount);
          } else {
            // Zero represents "everything"
            uint256 amount = transferDetail.amount == 0
              ? IERC20(swap.params.tokenOut).balanceOf(address(this))
              : transferDetail.amount;

            _sendERC20(transferDetail.recipient, amount, IERC20(swap.params.tokenOut));
          }

          unchecked {
            ++j;
          }
        }
      } catch {
        if (revertIfIncomplete) {
          revert UnsuccessfulFill();
        }
      }

      unchecked {
        ++i;
      }
    }
  }
}

File 105 of 129 : X2Y2Module.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {IX2Y2} from "../../../interfaces/IX2Y2.sol";

// Notes on the X2Y2 module:
// - supports filling listings (both ERC721/ERC1155 but only ETH-denominated)
// - supports filling offers (both ERC721/ERC1155)

contract X2Y2Module is BaseExchangeModule {
  using SafeERC20 for IERC20;

  // --- Fields ---

  IX2Y2 public immutable EXCHANGE;

  address public immutable ERC721_DELEGATE;
  address public immutable ERC1155_DELEGATE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange,
    address erc721Delegate,
    address erc1155Delegate
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = IX2Y2(exchange);
    ERC721_DELEGATE = erc721Delegate;
    ERC1155_DELEGATE = erc1155Delegate;
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    IX2Y2.RunInput calldata input,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buy(input, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    IX2Y2.RunInput[] calldata inputs,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // X2Y2 does not support batch filling so we fill orders one by one
    uint256 length = inputs.length;
    for (uint256 i = 0; i < length; ) {
      // Execute fill
      _buy(inputs[i], params.fillTo, params.revertIfIncomplete, inputs[i].details[0].price);

      unchecked {
        ++i;
      }
    }
  }

  // --- [ERC721] Single offer ---

  function acceptERC721Offer(
    IX2Y2.RunInput calldata input,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    if (input.details.length != 1) {
      revert WrongParams();
    }

    // Extract the order's corresponding token
    IX2Y2.SettleDetail calldata detail = input.details[0];
    IX2Y2.Order calldata order = input.orders[detail.orderIdx];
    IX2Y2.OrderItem calldata orderItem = order.items[detail.itemIdx];
    if (detail.op != IX2Y2.Op.COMPLETE_BUY_OFFER) {
      revert WrongParams();
    }

    // Apply any mask (if required)
    bytes memory data = orderItem.data;
    {
      if (order.dataMask.length > 0 && detail.dataReplacement.length > 0) {
        _arrayReplace(data, detail.dataReplacement, order.dataMask);
      }
    }

    IX2Y2.ERC721Pair[] memory pairs = abi.decode(orderItem.data, (IX2Y2.ERC721Pair[]));
    if (pairs.length != 1) {
      revert WrongParams();
    }

    IERC721 collection = pairs[0].token;
    uint256 tokenId = pairs[0].tokenId;

    // Approve the delegate if needed
    _approveERC721IfNeeded(collection, ERC721_DELEGATE);

    // Execute fill
    try EXCHANGE.run(input) {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, order.currency);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(params.fillTo, order.currency);
    } catch {
      // Revert if specified
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, collection, tokenId);
  }

  // --- [ERC1155] Single offer ---

  function acceptERC1155Offer(
    IX2Y2.RunInput calldata input,
    OfferParams calldata params,
    Fee[] calldata fees
  ) external nonReentrant {
    if (input.details.length != 1) {
      revert WrongParams();
    }

    // Extract the order's corresponding token
    IX2Y2.SettleDetail calldata detail = input.details[0];
    IX2Y2.Order calldata order = input.orders[detail.orderIdx];
    IX2Y2.OrderItem calldata orderItem = order.items[detail.itemIdx];
    if (detail.op != IX2Y2.Op.COMPLETE_BUY_OFFER) {
      revert WrongParams();
    }

    // Apply any mask (if required)
    bytes memory data = orderItem.data;
    {
      if (order.dataMask.length > 0 && detail.dataReplacement.length > 0) {
        _arrayReplace(data, detail.dataReplacement, order.dataMask);
      }
    }

    IX2Y2.ERC1155Pair[] memory pairs = abi.decode(orderItem.data, (IX2Y2.ERC1155Pair[]));
    if (pairs.length != 1) {
      revert WrongParams();
    }

    IERC1155 collection = pairs[0].token;
    uint256 tokenId = pairs[0].tokenId;

    // Approve the delegate if needed
    _approveERC1155IfNeeded(collection, ERC1155_DELEGATE);

    // Execute fill
    try EXCHANGE.run(input) {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, order.currency);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(params.fillTo, order.currency);
    } catch {
      // Revert if specified
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, collection, tokenId);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  function _arrayReplace(
    bytes memory source,
    bytes memory replacement,
    bytes memory mask
  ) internal view virtual {
    uint256 sourceLength = source.length;
    for (uint256 i; i < sourceLength; ) {
      if (mask[i] != 0) {
        source[i] = replacement[i];
      }

      unchecked {
        ++i;
      }
    }
  }

  function _buy(
    IX2Y2.RunInput calldata input,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    if (input.details.length != 1) {
      revert WrongParams();
    }

    // Extract the order's corresponding token
    IX2Y2.SettleDetail calldata detail = input.details[0];
    IX2Y2.Order calldata order = input.orders[detail.orderIdx];
    IX2Y2.OrderItem calldata orderItem = order.items[detail.itemIdx];
    if (detail.op != IX2Y2.Op.COMPLETE_SELL_OFFER) {
      revert WrongParams();
    }

    // Execute fill
    try EXCHANGE.run{value: value}(input) {
      if (order.delegateType == 1) {
        IX2Y2.ERC721Pair[] memory pairs = abi.decode(orderItem.data, (IX2Y2.ERC721Pair[]));
        if (pairs.length != 1) {
          revert WrongParams();
        }

        pairs[0].token.safeTransferFrom(address(this), receiver, pairs[0].tokenId);
      } else {
        IX2Y2.ERC1155Pair[] memory pairs = abi.decode(orderItem.data, (IX2Y2.ERC1155Pair[]));
        if (pairs.length != 1) {
          revert WrongParams();
        }

        pairs[0].token.safeTransferFrom(
          address(this),
          receiver,
          pairs[0].tokenId,
          pairs[0].amount,
          ""
        );
      }
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 106 of 129 : ZeroExV4Module.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {IZeroExV4} from "../../../interfaces/IZeroExV4.sol";

// Notes:
// - supports filling listings (both ERC721/ERC1155)
// - supports filling offers (both ERC721/ERC1155)

contract ZeroExV4Module is BaseExchangeModule {
  using SafeERC20 for IERC20;

  // --- Fields ---

  IZeroExV4 public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = IZeroExV4(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- [ERC721] Single ETH listing ---

  function acceptETHListingERC721(
    IZeroExV4.ERC721Order calldata order,
    IZeroExV4.Signature calldata signature,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buyERC721(order, signature, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- [ERC721] Single ERC20 listing ---

  function acceptERC20ListingERC721(
    IZeroExV4.ERC721Order calldata order,
    IZeroExV4.Signature calldata signature,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _buyERC721(order, signature, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC721] Multiple ETH listings ---

  function acceptETHListingsERC721(
    IZeroExV4.ERC721Order[] calldata orders,
    IZeroExV4.Signature[] calldata signatures,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buyERC721s(orders, signatures, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- [ERC721] Multiple ERC20 listings ---

  function acceptERC20ListingsERC721(
    IZeroExV4.ERC721Order[] calldata orders,
    IZeroExV4.Signature[] calldata signatures,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _buyERC721s(orders, signatures, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC1155] Single ETH listing ---

  function acceptETHListingERC1155(
    IZeroExV4.ERC1155Order calldata order,
    IZeroExV4.Signature calldata signature,
    uint128 amount,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buyERC1155(order, signature, amount, params.fillTo, params.revertIfIncomplete, params.amount);
  }

  // --- [ERC1155] Single ERC20 listing ---

  function acceptERC20ListingERC1155(
    IZeroExV4.ERC1155Order calldata order,
    IZeroExV4.Signature calldata signature,
    uint128 amount,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _buyERC1155(order, signature, amount, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC1155] Multiple ETH listings ---

  function acceptETHListingsERC1155(
    IZeroExV4.ERC1155Order[] calldata orders,
    IZeroExV4.Signature[] calldata signatures,
    uint128[] memory amounts,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buyERC1155s(
      orders,
      signatures,
      amounts,
      params.fillTo,
      params.revertIfIncomplete,
      params.amount
    );
  }

  // --- [ERC1155] Multiple ERC20 listings ---

  function acceptERC20ListingsERC1155(
    IZeroExV4.ERC1155Order[] calldata orders,
    IZeroExV4.Signature[] calldata signatures,
    uint128[] memory amounts,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Approve the exchange if needed
    _approveERC20IfNeeded(params.token, address(EXCHANGE), params.amount);

    // Execute fill
    _buyERC1155s(orders, signatures, amounts, params.fillTo, params.revertIfIncomplete, 0);
  }

  // --- [ERC721] Single offer ---

  function acceptERC721Offer(
    IZeroExV4.ERC721Order calldata order,
    IZeroExV4.Signature calldata signature,
    OfferParams calldata params,
    uint256 tokenId,
    Fee[] calldata fees
  ) external nonReentrant {
    // Approve the exchange if needed
    _approveERC721IfNeeded(order.erc721Token, address(EXCHANGE));

    // Execute fill
    try EXCHANGE.sellERC721(order, signature, tokenId, false, "") {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, order.erc20Token);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(params.fillTo, order.erc20Token);
    } catch {
      // Revert if specified
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any ERC721 leftover
    _sendAllERC721(params.refundTo, order.erc721Token, tokenId);
  }

  // --- [ERC1155] Single offer ---

  function acceptERC1155Offer(
    IZeroExV4.ERC1155Order calldata order,
    IZeroExV4.Signature calldata signature,
    uint128 amount,
    OfferParams calldata params,
    uint256 tokenId,
    Fee[] calldata fees
  ) external nonReentrant {
    // Approve the exchange if needed
    _approveERC1155IfNeeded(order.erc1155Token, address(EXCHANGE));

    // Execute fill
    try EXCHANGE.sellERC1155(order, signature, tokenId, amount, false, "") {
      // Pay fees
      uint256 feesLength = fees.length;
      for (uint256 i; i < feesLength; ) {
        Fee memory fee = fees[i];
        _sendERC20(fee.recipient, fee.amount, order.erc20Token);

        unchecked {
          ++i;
        }
      }

      // Forward any left payment to the specified receiver
      _sendAllERC20(params.fillTo, order.erc20Token);
    } catch {
      // Revert if specified
      if (params.revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }

    // Refund any ERC1155 leftover
    _sendAllERC1155(params.refundTo, order.erc1155Token, tokenId);
  }

  // --- ERC721 / ERC1155 hooks ---

  // Single token offer acceptance can be done approval-less by using the
  // standard `safeTransferFrom` method together with specifying data for
  // further contract calls. An example:
  // `safeTransferFrom(
  //      0xWALLET,
  //      0xMODULE,
  //      TOKEN_ID,
  //      0xABI_ENCODED_ROUTER_EXECUTION_CALLDATA_FOR_OFFER_ACCEPTANCE
  // )`

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256, // tokenId
    uint256, // amount
    bytes calldata data
  ) external returns (bytes4) {
    if (data.length > 0) {
      _makeCall(router, data, 0);
    }

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  function _buyERC721(
    IZeroExV4.ERC721Order calldata order,
    IZeroExV4.Signature calldata signature,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    // Execute fill
    try EXCHANGE.buyERC721{value: value}(order, signature, "") {
      order.erc721Token.safeTransferFrom(address(this), receiver, order.erc721TokenId);
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _buyERC1155(
    IZeroExV4.ERC1155Order calldata order,
    IZeroExV4.Signature calldata signature,
    uint128 amount,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    try EXCHANGE.buyERC1155{value: value}(order, signature, amount, "") {
      order.erc1155Token.safeTransferFrom(
        address(this),
        receiver,
        order.erc1155TokenId,
        amount,
        ""
      );
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _buyERC721s(
    IZeroExV4.ERC721Order[] calldata orders,
    IZeroExV4.Signature[] calldata signatures,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    uint256 length = orders.length;

    // Execute fill
    try
      EXCHANGE.batchBuyERC721s{value: value}(
        orders,
        signatures,
        new bytes[](length),
        revertIfIncomplete
      )
    returns (bool[] memory fulfilled) {
      for (uint256 i = 0; i < length; ) {
        if (fulfilled[i]) {
          orders[i].erc721Token.safeTransferFrom(address(this), receiver, orders[i].erc721TokenId);
        }

        unchecked {
          ++i;
        }
      }
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }

  function _buyERC1155s(
    IZeroExV4.ERC1155Order[] calldata orders,
    IZeroExV4.Signature[] calldata signatures,
    uint128[] memory amounts,
    address receiver,
    bool revertIfIncomplete,
    uint256 value
  ) internal {
    uint256 length = orders.length;

    uint128[] memory fillAmounts = new uint128[](length);
    for (uint256 i = 0; i < length; ) {
      fillAmounts[i] = amounts[i];

      unchecked {
        ++i;
      }
    }

    // Execute fill
    try
      EXCHANGE.batchBuyERC1155s{value: value}(
        orders,
        signatures,
        fillAmounts,
        new bytes[](length),
        revertIfIncomplete
      )
    returns (bool[] memory fulfilled) {
      for (uint256 i = 0; i < length; ) {
        if (fulfilled[i]) {
          orders[i].erc1155Token.safeTransferFrom(
            address(this),
            receiver,
            orders[i].erc1155TokenId,
            fillAmounts[i],
            ""
          );
        }

        unchecked {
          ++i;
        }
      }
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 107 of 129 : ZoraModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {BaseExchangeModule} from "./BaseExchangeModule.sol";
import {BaseModule} from "../BaseModule.sol";
import {IZora} from "../../../interfaces/IZora.sol";

// Notes:
// - supports filling "asks"

contract ZoraModule is BaseExchangeModule {
  // --- Structs ---

  struct Ask {
    IERC721 collection;
    uint256 tokenId;
    address currency;
    uint256 amount;
    address finder;
  }

  // --- Fields ---

  IZora public immutable EXCHANGE;

  // --- Constructor ---

  constructor(
    address owner,
    address router,
    address exchange
  ) BaseModule(owner) BaseExchangeModule(router) {
    EXCHANGE = IZora(exchange);
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Single ETH listing ---

  function acceptETHListing(
    Ask calldata ask,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Execute fill
    _buy(ask, params.fillTo, params.revertIfIncomplete);
  }

  // --- Multiple ETH listings ---

  function acceptETHListings(
    Ask[] calldata asks,
    ETHListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundETHLeftover(params.refundTo)
    chargeETHFees(fees, params.amount)
  {
    // Foundation does not support batch filling so we fill orders one by one
    for (uint256 i = 0; i < asks.length; ) {
      _buy(asks[i], params.fillTo, params.revertIfIncomplete);

      unchecked {
        ++i;
      }
    }
  }

  // --- Single ERC20 listing ---

  function acceptERC20Listing(
    Ask calldata ask,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Execute fill
    _buy(ask, params.fillTo, params.revertIfIncomplete);
  }

  // --- Multiple ERC20 listings ---

  function acceptERC20Listings(
    Ask[] calldata asks,
    ERC20ListingParams calldata params,
    Fee[] calldata fees
  )
    external
    payable
    nonReentrant
    refundERC20Leftover(params.refundTo, params.token)
    chargeERC20Fees(fees, params.token, params.amount)
  {
    // Foundation does not support batch filling so we fill orders one by one
    for (uint256 i = 0; i < asks.length; ) {
      _buy(asks[i], params.fillTo, params.revertIfIncomplete);

      unchecked {
        ++i;
      }
    }
  }

  // --- ERC721 hooks ---

  function onERC721Received(
    address, // operator,
    address, // from
    uint256, // tokenId,
    bytes calldata // data
  ) external pure returns (bytes4) {
    return this.onERC721Received.selector;
  }

  // --- Internal ---

  function _buy(Ask calldata ask, address receiver, bool revertIfIncomplete) internal {
    // Execute fill
    try
      EXCHANGE.fillAsk{value: ask.currency == address(0) ? ask.amount : 0}(
        address(ask.collection),
        ask.tokenId,
        ask.currency,
        ask.amount,
        ask.finder
      )
    {
      ask.collection.safeTransferFrom(address(this), receiver, ask.tokenId);
    } catch {
      // Revert if specified
      if (revertIfIncomplete) {
        revert UnsuccessfulFill();
      }
    }
  }
}

File 108 of 129 : MintModule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";

import {MintProxy} from "./MintProxy.sol";

contract MintModule {
  // --- Errors ---

  error UnsuccessfulCall();

  // --- Fields ---

  address public implementation;
  mapping(address => address) public proxies;

  // --- Constructor ---

  constructor() {
    implementation = address(new MintProxy());
  }

  // --- Methods ---

  function mint(address minter, bytes calldata data) external payable {
    address proxy = proxies[minter];
    if (proxy == address(0)) {
      proxy = Clones.cloneDeterministic(implementation, bytes32(uint256(uint160(minter))));
      MintProxy(proxy).initialize(minter);

      proxies[minter] = proxy;
    }

    (bool result, ) = proxy.call{value: msg.value}(data);
    if (!result) {
      revert UnsuccessfulCall();
    }
  }
}

File 109 of 129 : MintProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

contract MintProxy is ReentrancyGuard {
  // --- Structs ---

  struct Fee {
    address recipient;
    uint256 amount;
  }

  struct MintDetail {
    address to;
    bytes data;
    uint256 value;
    Fee[] fees;
    address token;
    uint256 quantity;
    string comment;
  }

  struct MintParams {
    address refundTo;
    bool revertIfIncomplete;
  }

  // --- Events ---

  event MintComment(address indexed token, uint256 quantity, string comment);

  // --- Errors ---

  error AlreadyInitialized();
  error Unauthorized();
  error UnsuccessfulCall();
  error UnsuccessfulMint();
  error UnsuccessfulPayment();

  // --- Fields ---

  address public owner;

  // --- Initializer ---

  function initialize(address _owner) external {
    if (owner != address(0)) {
      revert AlreadyInitialized();
    }

    owner = _owner;
  }

  // --- Methods ---

  function mintMultiple(
    MintDetail[] calldata mintDetails,
    MintParams calldata params
  ) external payable nonReentrant {
    uint256 length = mintDetails.length;
    for (uint256 i; i < length; ) {
      MintDetail calldata mintDetail = mintDetails[i];

      (bool result, ) = mintDetail.to.call{value: mintDetail.value}(mintDetail.data);
      if (!result && params.revertIfIncomplete) {
        revert UnsuccessfulMint();
      } else if (result) {
        Fee[] calldata fees = mintDetail.fees;

        uint256 feesLength = fees.length;
        for (uint256 j; j < feesLength; ) {
          _sendETH(fees[j].recipient, fees[j].amount);

          unchecked {
            ++j;
          }
        }

        if (bytes(mintDetail.comment).length > 0) {
          emit MintComment(mintDetail.token, mintDetail.quantity, mintDetail.comment);
        }
      }

      unchecked {
        ++i;
      }
    }

    uint256 leftover = address(this).balance;
    if (leftover > 0) {
      _sendETH(params.refundTo, leftover);
    }
  }

  function makeCalls(
    address[] calldata targets,
    bytes[] calldata data,
    uint256[] calldata values
  ) external payable nonReentrant {
    if (msg.sender != owner) {
      revert Unauthorized();
    }

    uint256 length = targets.length;
    for (uint256 i = 0; i < length; ) {
      (bool success, ) = payable(targets[i]).call{value: values[i]}(data[i]);
      if (!success) {
        revert UnsuccessfulCall();
      }

      unchecked {
        ++i;
      }
    }
  }

  // --- ERC721 / ERC1155 hooks ---

  function onERC721Received(
    address, // operator
    address, // from
    uint256 tokenId,
    bytes calldata // data
  ) external returns (bytes4) {
    IERC721(msg.sender).safeTransferFrom(address(this), owner, tokenId);

    return this.onERC721Received.selector;
  }

  function onERC1155Received(
    address, // operator
    address, // from
    uint256 tokenId,
    uint256 amount,
    bytes calldata // data
  ) external returns (bytes4) {
    IERC1155(msg.sender).safeTransferFrom(address(this), owner, tokenId, amount, "");

    return this.onERC1155Received.selector;
  }

  // --- Internal ---

  function _sendETH(address to, uint256 amount) internal {
    if (amount > 0) {
      (bool success, ) = payable(to).call{value: amount}("");
      if (!success) {
        revert UnsuccessfulPayment();
      }
    }
  }
}

File 110 of 129 : ReservoirV6_0_1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

contract ReservoirV6_0_1 is ReentrancyGuard {
  using Address for address;

  // --- Structs ---

  struct ExecutionInfo {
    address module;
    bytes data;
    uint256 value;
  }

  struct AmountCheckInfo {
    address target;
    bytes data;
    uint256 threshold;
  }

  // --- Errors ---

  error UnsuccessfulExecution();
  error UnsuccessfulPayment();

  // --- Modifiers ---

  modifier refundETH() {
    _;

    uint256 leftover = address(this).balance;
    if (leftover > 0) {
      (bool success, ) = payable(msg.sender).call{value: leftover}("");
      if (!success) {
        revert UnsuccessfulPayment();
      }
    }
  }

  // --- Fallback ---

  receive() external payable {}

  // --- Public ---

  // Trigger a set of executions atomically
  function execute(
    ExecutionInfo[] calldata executionInfos
  ) external payable nonReentrant refundETH {
    uint256 length = executionInfos.length;
    for (uint256 i = 0; i < length; ) {
      _executeInternal(executionInfos[i]);

      unchecked {
        ++i;
      }
    }
  }

  // Trigger a set of executions with amount checking. As opposed to the regular
  // `execute` method, `executeWithAmountCheck` supports stopping the executions
  // once the provided amount check reaches a certain value. This is useful when
  // trying to fill orders with slippage (eg. provide multiple orders and try to
  // fill until a certain balance is reached). In order to be flexible, checking
  // the amount is done generically by calling the `target` contract with `data`.
  // For example, this could be used to check the ERC721 total owned balance (by
  // using `balanceOf(owner)`), the ERC1155 total owned balance per token id (by
  // using `balanceOf(owner, tokenId)`), but also for checking the ERC1155 total
  // owned balance per multiple token ids (by using a custom contract that wraps
  // `balanceOfBatch(owners, tokenIds)`).
  function executeWithAmountCheck(
    ExecutionInfo[] calldata executionInfos,
    AmountCheckInfo calldata amountCheckInfo
  ) external payable nonReentrant refundETH {
    // Cache some data for efficiency
    address target = amountCheckInfo.target;
    bytes calldata data = amountCheckInfo.data;
    uint256 threshold = amountCheckInfo.threshold;

    uint256 length = executionInfos.length;
    for (uint256 i = 0; i < length; ) {
      // Check the amount and break if it exceeds the threshold
      uint256 amount = _getAmount(target, data);
      if (amount >= threshold) {
        break;
      }

      _executeInternal(executionInfos[i]);

      unchecked {
        ++i;
      }
    }
  }

  // --- Internal ---

  function _executeInternal(ExecutionInfo calldata executionInfo) internal {
    address module = executionInfo.module;

    // Ensure the target is a contract
    if (!module.isContract()) {
      revert UnsuccessfulExecution();
    }

    (bool success, ) = module.call{value: executionInfo.value}(executionInfo.data);
    if (!success) {
      revert UnsuccessfulExecution();
    }
  }

  function _getAmount(address target, bytes calldata data) internal view returns (uint256 amount) {
    // Ensure the target is a contract
    if (!target.isContract()) {
      revert UnsuccessfulExecution();
    }

    (bool success, bytes memory result) = target.staticcall(data);
    if (!success) {
      revert UnsuccessfulExecution();
    }

    amount = abi.decode(result, (uint256));
  }
}

File 111 of 129 : ZoneInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { ZoneParameters, Schema } from "../lib/ConsiderationStructs.sol";

interface ZoneInterface {
    function validateOrder(
        ZoneParameters calldata zoneParameters
    ) external returns (bytes4 validOrderMagicValue);

    function getSeaportMetadata()
        external
        view
        returns (
            string memory name,
            Schema[] memory schemas // map to Seaport Improvement Proposal IDs
        );
}

File 112 of 129 : ConsiderationEnums.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

// prettier-ignore
enum OrderType {
    // 0: no partial fills, anyone can execute
    FULL_OPEN,

    // 1: partial fills supported, anyone can execute
    PARTIAL_OPEN,

    // 2: no partial fills, only offerer or zone can execute
    FULL_RESTRICTED,

    // 3: partial fills supported, only offerer or zone can execute
    PARTIAL_RESTRICTED,

    // 4: contract order type
    CONTRACT
}

// prettier-ignore
enum BasicOrderType {
    // 0: no partial fills, anyone can execute
    ETH_TO_ERC721_FULL_OPEN,

    // 1: partial fills supported, anyone can execute
    ETH_TO_ERC721_PARTIAL_OPEN,

    // 2: no partial fills, only offerer or zone can execute
    ETH_TO_ERC721_FULL_RESTRICTED,

    // 3: partial fills supported, only offerer or zone can execute
    ETH_TO_ERC721_PARTIAL_RESTRICTED,

    // 4: no partial fills, anyone can execute
    ETH_TO_ERC1155_FULL_OPEN,

    // 5: partial fills supported, anyone can execute
    ETH_TO_ERC1155_PARTIAL_OPEN,

    // 6: no partial fills, only offerer or zone can execute
    ETH_TO_ERC1155_FULL_RESTRICTED,

    // 7: partial fills supported, only offerer or zone can execute
    ETH_TO_ERC1155_PARTIAL_RESTRICTED,

    // 8: no partial fills, anyone can execute
    ERC20_TO_ERC721_FULL_OPEN,

    // 9: partial fills supported, anyone can execute
    ERC20_TO_ERC721_PARTIAL_OPEN,

    // 10: no partial fills, only offerer or zone can execute
    ERC20_TO_ERC721_FULL_RESTRICTED,

    // 11: partial fills supported, only offerer or zone can execute
    ERC20_TO_ERC721_PARTIAL_RESTRICTED,

    // 12: no partial fills, anyone can execute
    ERC20_TO_ERC1155_FULL_OPEN,

    // 13: partial fills supported, anyone can execute
    ERC20_TO_ERC1155_PARTIAL_OPEN,

    // 14: no partial fills, only offerer or zone can execute
    ERC20_TO_ERC1155_FULL_RESTRICTED,

    // 15: partial fills supported, only offerer or zone can execute
    ERC20_TO_ERC1155_PARTIAL_RESTRICTED,

    // 16: no partial fills, anyone can execute
    ERC721_TO_ERC20_FULL_OPEN,

    // 17: partial fills supported, anyone can execute
    ERC721_TO_ERC20_PARTIAL_OPEN,

    // 18: no partial fills, only offerer or zone can execute
    ERC721_TO_ERC20_FULL_RESTRICTED,

    // 19: partial fills supported, only offerer or zone can execute
    ERC721_TO_ERC20_PARTIAL_RESTRICTED,

    // 20: no partial fills, anyone can execute
    ERC1155_TO_ERC20_FULL_OPEN,

    // 21: partial fills supported, anyone can execute
    ERC1155_TO_ERC20_PARTIAL_OPEN,

    // 22: no partial fills, only offerer or zone can execute
    ERC1155_TO_ERC20_FULL_RESTRICTED,

    // 23: partial fills supported, only offerer or zone can execute
    ERC1155_TO_ERC20_PARTIAL_RESTRICTED
}

// prettier-ignore
enum BasicOrderRouteType {
    // 0: provide Ether (or other native token) to receive offered ERC721 item.
    ETH_TO_ERC721,

    // 1: provide Ether (or other native token) to receive offered ERC1155 item.
    ETH_TO_ERC1155,

    // 2: provide ERC20 item to receive offered ERC721 item.
    ERC20_TO_ERC721,

    // 3: provide ERC20 item to receive offered ERC1155 item.
    ERC20_TO_ERC1155,

    // 4: provide ERC721 item to receive offered ERC20 item.
    ERC721_TO_ERC20,

    // 5: provide ERC1155 item to receive offered ERC20 item.
    ERC1155_TO_ERC20
}

// prettier-ignore
enum ItemType {
    // 0: ETH on mainnet, MATIC on polygon, etc.
    NATIVE,

    // 1: ERC20 items (ERC777 and ERC20 analogues could also technically work)
    ERC20,

    // 2: ERC721 items
    ERC721,

    // 3: ERC1155 items
    ERC1155,

    // 4: ERC721 items where a number of tokenIds are supported
    ERC721_WITH_CRITERIA,

    // 5: ERC1155 items where a number of ids are supported
    ERC1155_WITH_CRITERIA
}

// prettier-ignore
enum Side {
    // 0: Items that can be spent
    OFFER,

    // 1: Items that must be received
    CONSIDERATION
}

File 113 of 129 : ConsiderationStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { OrderType, BasicOrderType, ItemType, Side } from "./ConsiderationEnums.sol";

/**
 * @dev An order contains eleven components: an offerer, a zone (or account that
 *      can cancel the order or restrict who can fulfill the order depending on
 *      the type), the order type (specifying partial fill support as well as
 *      restricted order status), the start and end time, a hash that will be
 *      provided to the zone when validating restricted orders, a salt, a key
 *      corresponding to a given conduit, a counter, and an arbitrary number of
 *      offer items that can be spent along with consideration items that must
 *      be received by their respective recipient.
 */
struct OrderComponents {
    address offerer;
    address zone;
    OfferItem[] offer;
    ConsiderationItem[] consideration;
    OrderType orderType;
    uint256 startTime;
    uint256 endTime;
    bytes32 zoneHash;
    uint256 salt;
    bytes32 conduitKey;
    uint256 counter;
}

/**
 * @dev An offer item has five components: an item type (ETH or other native
 *      tokens, ERC20, ERC721, and ERC1155, as well as criteria-based ERC721 and
 *      ERC1155), a token address, a dual-purpose "identifierOrCriteria"
 *      component that will either represent a tokenId or a merkle root
 *      depending on the item type, and a start and end amount that support
 *      increasing or decreasing amounts over the duration of the respective
 *      order.
 */
struct OfferItem {
    ItemType itemType;
    address token;
    uint256 identifierOrCriteria;
    uint256 startAmount;
    uint256 endAmount;
}

/**
 * @dev A consideration item has the same five components as an offer item and
 *      an additional sixth component designating the required recipient of the
 *      item.
 */
struct ConsiderationItem {
    ItemType itemType;
    address token;
    uint256 identifierOrCriteria;
    uint256 startAmount;
    uint256 endAmount;
    address payable recipient;
}

/**
 * @dev A spent item is translated from a utilized offer item and has four
 *      components: an item type (ETH or other native tokens, ERC20, ERC721, and
 *      ERC1155), a token address, a tokenId, and an amount.
 */
struct SpentItem {
    ItemType itemType;
    address token;
    uint256 identifier;
    uint256 amount;
}

/**
 * @dev A received item is translated from a utilized consideration item and has
 *      the same four components as a spent item, as well as an additional fifth
 *      component designating the required recipient of the item.
 */
struct ReceivedItem {
    ItemType itemType;
    address token;
    uint256 identifier;
    uint256 amount;
    address payable recipient;
}

/**
 * @dev For basic orders involving ETH / native / ERC20 <=> ERC721 / ERC1155
 *      matching, a group of six functions may be called that only requires a
 *      subset of the usual order arguments. Note the use of a "basicOrderType"
 *      enum; this represents both the usual order type as well as the "route"
 *      of the basic order (a simple derivation function for the basic order
 *      type is `basicOrderType = orderType + (4 * basicOrderRoute)`.)
 */
struct BasicOrderParameters {
    // calldata offset
    address considerationToken; // 0x24
    uint256 considerationIdentifier; // 0x44
    uint256 considerationAmount; // 0x64
    address payable offerer; // 0x84
    address zone; // 0xa4
    address offerToken; // 0xc4
    uint256 offerIdentifier; // 0xe4
    uint256 offerAmount; // 0x104
    BasicOrderType basicOrderType; // 0x124
    uint256 startTime; // 0x144
    uint256 endTime; // 0x164
    bytes32 zoneHash; // 0x184
    uint256 salt; // 0x1a4
    bytes32 offererConduitKey; // 0x1c4
    bytes32 fulfillerConduitKey; // 0x1e4
    uint256 totalOriginalAdditionalRecipients; // 0x204
    AdditionalRecipient[] additionalRecipients; // 0x224
    bytes signature; // 0x244
    // Total length, excluding dynamic array data: 0x264 (580)
}

/**
 * @dev Basic orders can supply any number of additional recipients, with the
 *      implied assumption that they are supplied from the offered ETH (or other
 *      native token) or ERC20 token for the order.
 */
struct AdditionalRecipient {
    uint256 amount;
    address payable recipient;
}

/**
 * @dev The full set of order components, with the exception of the counter,
 *      must be supplied when fulfilling more sophisticated orders or groups of
 *      orders. The total number of original consideration items must also be
 *      supplied, as the caller may specify additional consideration items.
 */
struct OrderParameters {
    address offerer; // 0x00
    address zone; // 0x20
    OfferItem[] offer; // 0x40
    ConsiderationItem[] consideration; // 0x60
    OrderType orderType; // 0x80
    uint256 startTime; // 0xa0
    uint256 endTime; // 0xc0
    bytes32 zoneHash; // 0xe0
    uint256 salt; // 0x100
    bytes32 conduitKey; // 0x120
    uint256 totalOriginalConsiderationItems; // 0x140
    // offer.length                          // 0x160
}

/**
 * @dev Orders require a signature in addition to the other order parameters.
 */
struct Order {
    OrderParameters parameters;
    bytes signature;
}

/**
 * @dev Advanced orders include a numerator (i.e. a fraction to attempt to fill)
 *      and a denominator (the total size of the order) in addition to the
 *      signature and other order parameters. It also supports an optional field
 *      for supplying extra data; this data will be provided to the zone if the
 *      order type is restricted and the zone is not the caller, or will be
 *      provided to the offerer as context for contract order types.
 */
struct AdvancedOrder {
    OrderParameters parameters;
    uint120 numerator;
    uint120 denominator;
    bytes signature;
    bytes extraData;
}

/**
 * @dev Orders can be validated (either explicitly via `validate`, or as a
 *      consequence of a full or partial fill), specifically cancelled (they can
 *      also be cancelled in bulk via incrementing a per-zone counter), and
 *      partially or fully filled (with the fraction filled represented by a
 *      numerator and denominator).
 */
struct OrderStatus {
    bool isValidated;
    bool isCancelled;
    uint120 numerator;
    uint120 denominator;
}

/**
 * @dev A criteria resolver specifies an order, side (offer vs. consideration),
 *      and item index. It then provides a chosen identifier (i.e. tokenId)
 *      alongside a merkle proof demonstrating the identifier meets the required
 *      criteria.
 */
struct CriteriaResolver {
    uint256 orderIndex;
    Side side;
    uint256 index;
    uint256 identifier;
    bytes32[] criteriaProof;
}

/**
 * @dev A fulfillment is applied to a group of orders. It decrements a series of
 *      offer and consideration items, then generates a single execution
 *      element. A given fulfillment can be applied to as many offer and
 *      consideration items as desired, but must contain at least one offer and
 *      at least one consideration that match. The fulfillment must also remain
 *      consistent on all key parameters across all offer items (same offerer,
 *      token, type, tokenId, and conduit preference) as well as across all
 *      consideration items (token, type, tokenId, and recipient).
 */
struct Fulfillment {
    FulfillmentComponent[] offerComponents;
    FulfillmentComponent[] considerationComponents;
}

/**
 * @dev Each fulfillment component contains one index referencing a specific
 *      order and another referencing a specific offer or consideration item.
 */
struct FulfillmentComponent {
    uint256 orderIndex;
    uint256 itemIndex;
}

/**
 * @dev An execution is triggered once all consideration items have been zeroed
 *      out. It sends the item in question from the offerer to the item's
 *      recipient, optionally sourcing approvals from either this contract
 *      directly or from the offerer's chosen conduit if one is specified. An
 *      execution is not provided as an argument, but rather is derived via
 *      orders, criteria resolvers, and fulfillments (where the total number of
 *      executions will be less than or equal to the total number of indicated
 *      fulfillments) and returned as part of `matchOrders`.
 */
struct Execution {
    ReceivedItem item;
    address offerer;
    bytes32 conduitKey;
}

/**
 * @dev Restricted orders are validated post-execution by calling validateOrder
 *      on the zone. This struct provides context about the order fulfillment
 *      and any supplied extraData, as well as all order hashes fulfilled in a
 *      call to a match or fulfillAvailable method.
 */
struct ZoneParameters {
    bytes32 orderHash;
    address fulfiller;
    address offerer;
    SpentItem[] offer;
    ReceivedItem[] consideration;
    bytes extraData;
    bytes32[] orderHashes;
    uint256 startTime;
    uint256 endTime;
    bytes32 zoneHash;
}

/**
 * @dev Zones and contract offerers can communicate which schemas they implement
 *      along with any associated metadata related to each schema.
 */
struct Schema {
    uint256 id;
    bytes metadata;
}

File 114 of 129 : SignedZoneControllerEventsAndErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * @notice SignedZoneControllerEventsAndErrors contains errors and events
 *         related to deploying and managing new signed zones.
 */
interface SignedZoneControllerEventsAndErrors {
    /**
     * @dev Emit an event whenever a new zone is created.
     *
     * @param zoneAddress The address of the zone.
     * @param zoneName    The name for the zone returned in
     *                    getSeaportMetadata().
     * @param apiEndpoint The API endpoint where orders for this zone can be
     *                    signed.
     * @param documentationURI The URI to the documentation describing the
     *                         behavior of the contract.
     *                    Request and response payloads are defined in SIP-7.
     * @param salt        The salt used to deploy the zone.
     */
    event ZoneCreated(
        address zoneAddress,
        string zoneName,
        string apiEndpoint,
        string documentationURI,
        bytes32 salt
    );

    /**
     * @dev Emit an event whenever zone ownership is transferred.
     *
     * @param zone          The zone for which ownership has been
     *                      transferred.
     * @param previousOwner The previous owner of the zone.
     * @param newOwner      The new owner of the zone.
     */
    event OwnershipTransferred(
        address indexed zone,
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev Emit an event whenever a zone owner registers a new potential
     *      owner for that zone.
     *
     * @param newPotentialOwner The new potential owner of the zone.
     */
    event PotentialOwnerUpdated(address indexed newPotentialOwner);

    /**
     * @dev Emit an event when a signer has been updated.
     */
    event SignerUpdated(address signedZone, address signer, bool active);

    /**
     * @dev Revert with an error when attempting to update channels or transfer
     *      ownership of a zone when the caller is not the owner of the
     *      zone in question.
     */
    error CallerIsNotOwner(address zone);

    /**
     * @dev Revert with an error when the caller is not the owner or an active
     *      signer of the signed zone in question.
     */
    error CallerIsNotOwnerOrSigner(address zone);

    /**
     * @dev Revert with an error when attempting to claim ownership of a zone
     *      with a caller that is not the current potential owner for the
     *      zone in question.
     */
    error CallerIsNotNewPotentialOwner(address zone);

    /**
     * @dev Revert with an error when attempting to create a new signed zone
     *      using a salt where the first twenty bytes do not match the address
     *      of the caller or are not set to zero.
     */
    error InvalidCreator();

    /**
     * @dev Revert with an error when attempting to create a new zone when no
     *      initial owner address is supplied.
     */
    error InvalidInitialOwner();

    /**
     * @dev Revert with an error when attempting to set a new potential owner
     *      that is already set.
     */
    error NewPotentialOwnerAlreadySet(address zone, address newPotentialOwner);

    /**
     * @dev Revert with an error when attempting to cancel ownership transfer
     *      when no new potential owner is currently set.
     */
    error NoPotentialOwnerCurrentlySet(address zone);
    /**
     * @dev Revert with an error when attempting to register a new potential
     *      owner and supplying the null address.
     */
    error NewPotentialOwnerIsZeroAddress(address zone);

    /**
     * @dev Revert with an error when attempting to interact with a zone that
     *      does not yet exist.
     */
    error NoZone();

    /**
     * @dev Revert with an error if trying to add a signer that is
     *      already active.
     */
    error SignerAlreadyAdded(address signer);

    /**
     * @dev Revert with an error if a new signer is the zero address.
     */
    error SignerCannotBeZeroAddress();

    /**
     * @dev Revert with an error if a removed signer is trying to be
     *      reauthorized.
     */
    error SignerCannotBeReauthorized(address signer);

    /**
     * @dev Revert with an error if trying to remove a signer that is
     *      not present.
     */
    error SignerNotPresent(address signer);

    /**
     * @dev Revert with an error when attempting to deploy a zone that is
     *      currently deployed.
     */
    error ZoneAlreadyExists(address zone);
}

File 115 of 129 : SignedZoneControllerInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * @title  SignedZoneControllerInterface
 * @author BCLeFevre
 * @notice SignedZoneControllerInterface enables the deploying of SignedZones.
 *         SignedZones are an implementation of SIP-7 that requires orders
 *         to be signed by an approved signer.
 *         https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md
 *
 */
interface SignedZoneControllerInterface {
    /**
     * @notice Deploy a SignedZone to a precomputed address.
     *
     * @param zoneName    The name for the zone returned in
     *                    getSeaportMetadata().
     * @param apiEndpoint The API endpoint where orders for this zone can be
     *                    signed.
     * @param documentationURI The URI to the documentation describing the
     *                         behavior of the contract.
     *                    Request and response payloads are defined in SIP-7.
     * @param salt        The salt to be used to derive the zone address
     * @param initialOwner The initial owner to set for the new zone.
     *
     * @return derivedAddress The derived address for the zone.
     */
    function createZone(
        string memory zoneName,
        string memory apiEndpoint,
        string memory documentationURI,
        address initialOwner,
        bytes32 salt
    ) external returns (address derivedAddress);

    /**
     * @notice Returns the active signers for the zone.
     *
     * @param signedZone The signed zone to get the active signers for.
     *
     * @return signers The active signers.
     */
    function getActiveSigners(address signedZone)
        external
        view
        returns (address[] memory signers);

    /**
     * @notice Returns additional information about the zone.
     *
     * @param zone The zone to get the additional information for.
     *
     * @return domainSeparator  The domain separator used for signing.
     * @return zoneName         The name of the zone.
     * @return apiEndpoint      The API endpoint for the zone.
     * @return substandards     The substandards supported by the zone.
     * @return documentationURI The documentation URI for the zone.
     */
    function getAdditionalZoneInformation(address zone)
        external
        view
        returns (
            bytes32 domainSeparator,
            string memory zoneName,
            string memory apiEndpoint,
            uint256[] memory substandards,
            string memory documentationURI
        );

    /**
     * @notice Update the API endpoint returned by the supplied zone.
     *         Only the owner or an active signer can call this function.
     *
     * @param signedZone    The signed zone to update the API endpoint for.
     * @param newApiEndpoint The new API endpoint.
     */
    function updateAPIEndpoint(
        address signedZone,
        string calldata newApiEndpoint
    ) external;

    /**
     * @notice Update the signer for a given signed zone.
     *
     * @param signedZone The signed zone to update the signer for.
     * @param signer     The signer to update.
     * @param active     If the signer should be active or not.
     */
    function updateSigner(
        address signedZone,
        address signer,
        bool active
    ) external;

    /**
     * @notice Initiate zone ownership transfer by assigning a new potential
     *         owner for the given zone. Once set, the new potential owner
     *         may call `acceptOwnership` to claim ownership of the zone.
     *         Only the owner of the zone in question may call this function.
     *
     * @param zone The zone for which to initiate ownership transfer.
     * @param newPotentialOwner The new potential owner of the zone.
     */
    function transferOwnership(address zone, address newPotentialOwner)
        external;

    /**
     * @notice Clear the currently set potential owner, if any, from a zone.
     *         Only the owner of the zone in question may call this function.
     *
     * @param zone The zone for which to cancel ownership transfer.
     */
    function cancelOwnershipTransfer(address zone) external;

    /**
     * @notice Accept ownership of a supplied zone. Only accounts that the
     *         current owner has set as the new potential owner may call this
     *         function.
     *
     * @param zone The zone for which to accept ownership.
     */
    function acceptOwnership(address zone) external;

    /**
     * @notice Retrieve the current owner of a deployed zone.
     *
     * @param zone The zone for which to retrieve the associated owner.
     *
     * @return owner The owner of the supplied zone.
     */
    function ownerOf(address zone) external view returns (address owner);

    /**
     * @notice Retrieve the potential owner, if any, for a given zone. The
     *         current owner may set a new potential owner via
     *         `transferOwnership` and that owner may then accept ownership of
     *         the zone in question via `acceptOwnership`.
     *
     * @param zone The zone for which to retrieve the potential owner.
     *
     * @return potentialOwner The potential owner, if any, for the zone.
     */
    function getPotentialOwner(address zone)
        external
        view
        returns (address potentialOwner);

    /**
     * @notice Derive the zone address associated with a salt.
     *
     * @param salt        The salt to be used to derive the zone address
     *
     * @return derivedAddress The derived address of the signed zone.
     */
    function getZone(bytes32 salt)
        external
        view
        returns (address derivedAddress);
}

File 116 of 129 : SignedZoneEventsAndErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * @notice SignedZoneEventsAndErrors contains errors and events
 *         related to zone interaction.
 */
interface SignedZoneEventsAndErrors {
    /**
     * @dev Emit an event when a new signer is added.
     */
    event SignerAdded(address signer);

    /**
     * @dev Emit an event when a signer is removed.
     */
    event SignerRemoved(address signer);

    /**
     * @dev Revert with an error if msg.sender is not the owner
     *      or an active signer.
     */
    error OnlyOwnerOrActiveSigner();

    /**
     * @dev Revert with an error when the signature has expired.
     */
    error SignatureExpired(uint256 expiration, bytes32 orderHash);

    /**
     * @dev Revert with an error when attempting to update the signers of a
     *      the zone from a caller that is not the zone's controller.
     */
    error InvalidController();

    /**
     * @dev Revert with an error if supplied order extraData is an invalid
     *      length.
     */
    error InvalidExtraDataLength(bytes32 orderHash);

    /**
     * @dev Revert with an error if the supplied order extraData does not
     *      support the zone's SIP6 version.
     */
    error InvalidSIP6Version(bytes32 orderHash);

    /**
     * @dev Revert with an error if the supplied order extraData does not
     *      support the zone's substandard requirements.
     */
    error InvalidSubstandardSupport(
        string reason,
        uint256 substandardVersion,
        bytes32 orderHash
    );

    /**
     * @dev Revert with an error if the supplied order extraData does not
     *      support the zone's substandard version.
     */
    error InvalidSubstandardVersion(bytes32 orderHash);

    /**
     * @dev Revert with an error if the fulfiller does not match.
     */
    error InvalidFulfiller(
        address expectedFulfiller,
        address actualFulfiller,
        bytes32 orderHash
    );

    /**
     * @dev Revert with an error if the consideration does not match.
     */
    error InvalidConsideration(
        uint256 expectedConsiderationHash,
        uint256 actualConsiderationHash,
        bytes32 orderHash
    );

    /**
     * @dev Revert with an error if the zone parameter encoding is invalid.
     */
    error InvalidZoneParameterEncoding();

    /**
     * @dev Revert with an error when an order is signed with a signer
     *      that is not active.
     */
    error SignerNotActive(address signer, bytes32 orderHash);
}

File 117 of 129 : SignedZoneInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * @title  SignedZone
 * @author ryanio, BCLeFevre
 * @notice SignedZone is an implementation of SIP-7 that requires orders
 *         to be signed by an approved signer.
 *         https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md
 *
 */
interface SignedZoneInterface {
    /**
     * @notice Update the active status of a signer.
     *
     * @param signer The signer address to update.
     * @param active The new active status of the signer.
     */
    function updateSigner(address signer, bool active) external;

    /**
     * @notice Returns the active signers for the zone.
     *
     * @return signers The active signers.
     */
    function getActiveSigners()
        external
        view
        returns (address[] memory signers);
}

File 118 of 129 : SIP5Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { Schema } from "../../lib/ConsiderationStructs.sol";

/**
 * @dev SIP-5: Contract Metadata Interface for Seaport Contracts
 *      https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-5.md
 */
interface SIP5Interface {
    /**
     * @dev An event that is emitted when a SIP-5 compatible contract is deployed.
     */
    event SeaportCompatibleContractDeployed();

    /**
     * @dev Returns Seaport metadata for this contract, returning the
     *      contract name and supported schemas.
     *
     * @return name    The contract name
     * @return schemas The supported SIPs
     */
    function getSeaportMetadata()
        external
        view
        returns (string memory name, Schema[] memory schemas);
}

File 119 of 129 : SignedZoneConstants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/// @dev ECDSA signature offsets.
uint256 constant ECDSA_MaxLength = 65;
uint256 constant ECDSA_signature_s_offset = 0x40;
uint256 constant ECDSA_signature_v_offset = 0x60;

/// @dev Helpers for memory offsets.
uint256 constant OneWord = 0x20;
uint256 constant TwoWords = 0x40;
uint256 constant ThreeWords = 0x60;
uint256 constant FourWords = 0x80;
uint256 constant FiveWords = 0xa0;
uint256 constant Signature_lower_v = 27;
uint256 constant MaxUint8 = 0xff;
bytes32 constant EIP2098_allButHighestBitMask = (
    0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
);
uint256 constant Ecrecover_precompile = 1;
uint256 constant Ecrecover_args_size = 0x80;
uint256 constant FreeMemoryPointerSlot = 0x40;
uint256 constant ZeroSlot = 0x60;
uint256 constant Slot0x80 = 0x80;

/// @dev The EIP-712 digest offsets.
uint256 constant EIP712_DomainSeparator_offset = 0x02;
uint256 constant EIP712_SignedOrderHash_offset = 0x22;
uint256 constant EIP712_DigestPayload_size = 0x42;
uint256 constant EIP_712_PREFIX = (
    0x1901000000000000000000000000000000000000000000000000000000000000
);

/*
 *  error InvalidController()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 * Revert buffer is memory[0x1c:0x20]
 */
uint256 constant InvalidController_error_selector = 0x6d5769be;
uint256 constant InvalidController_error_length = 0x04;

/*
 *  error InvalidFulfiller(address expectedFulfiller, address actualFulfiller, bytes32 orderHash)
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: expectedFulfiller
 *    - 0x40: actualFullfiller
 *    - 0x60: orderHash
 * Revert buffer is memory[0x1c:0x80]
 */
uint256 constant InvalidFulfiller_error_selector = 0x1bcf9bb7;
uint256 constant InvalidFulfiller_error_expectedFulfiller_ptr = 0x20;
uint256 constant InvalidFulfiller_error_actualFulfiller_ptr = 0x40;
uint256 constant InvalidFulfiller_error_orderHash_ptr = 0x60;
uint256 constant InvalidFulfiller_error_length = 0x64;

/*
 *  error InvalidConsideration(uint256 expectedConsideration, uint256 actualConsideration, bytes32 orderHash)
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: expectedConsideration
 *    - 0x40: actualConsideration
 *    - 0x60: orderHash
 * Revert buffer is memory[0x1c:0x80]
 */
uint256 constant InvalidConsideration_error_selector = 0x59cb96d1;
uint256 constant InvalidConsideration_error_expectedConsideration_ptr = 0x20;
uint256 constant InvalidConsideration_error_actualConsideration_ptr = 0x40;
uint256 constant InvalidConsideration_error_orderHash_ptr = 0x60;
uint256 constant InvalidConsideration_error_length = 0x64;

/*
 *  error InvalidZoneParameterEncoding()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 * Revert buffer is memory[0x1c:0x20]
 */
uint256 constant InvalidZoneParameterEncoding_error_selector = 0x46d5d895;
uint256 constant InvalidZoneParameterEncoding_error_length = 0x04;

/*
 * error InvalidExtraDataLength()
 *   - Defined in SignedZoneEventsAndErrors.sol
 * Memory layout:
 *   - 0x00: Left-padded selector (data begins at 0x1c)
 *   - 0x20: orderHash
 * Revert buffer is memory[0x1c:0x40]
 */
uint256 constant InvalidExtraDataLength_error_selector = 0xd232fd2c;
uint256 constant InvalidExtraDataLength_error_orderHash_ptr = 0x20;
uint256 constant InvalidExtraDataLength_error_length = 0x24;
uint256 constant InvalidExtraDataLength_epected_length = 0x7e;

uint256 constant ExtraData_expiration_offset = 0x35;
uint256 constant ExtraData_substandard_version_byte_offset = 0x7d;
/*
 *  error InvalidSIP6Version()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: orderHash
 * Revert buffer is memory[0x1c:0x40]
 */
uint256 constant InvalidSIP6Version_error_selector = 0x64115774;
uint256 constant InvalidSIP6Version_error_orderHash_ptr = 0x20;
uint256 constant InvalidSIP6Version_error_length = 0x24;

/*
 *  error InvalidSubstandardVersion()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: orderHash
 * Revert buffer is memory[0x1c:0x40]
 */
uint256 constant InvalidSubstandardVersion_error_selector = 0x26787999;
uint256 constant InvalidSubstandardVersion_error_orderHash_ptr = 0x20;
uint256 constant InvalidSubstandardVersion_error_length = 0x24;

/*
 *  error InvalidSubstandardSupport()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: reason
 *    - 0x40: substandardVersion
 *    - 0x60: orderHash
 * Revert buffer is memory[0x1c:0xe0]
 */
uint256 constant InvalidSubstandardSupport_error_selector = 0x2be76224;
uint256 constant InvalidSubstandardSupport_error_reason_offset_ptr = 0x20;
uint256 constant InvalidSubstandardSupport_error_substandard_version_ptr = 0x40;
uint256 constant InvalidSubstandardSupport_error_orderHash_ptr = 0x60;
uint256 constant InvalidSubstandardSupport_error_reason_length_ptr = 0x80;
uint256 constant InvalidSubstandardSupport_error_reason_ptr = 0xa0;
uint256 constant InvalidSubstandardSupport_error_reason_2_ptr = 0xc0;
uint256 constant InvalidSubstandardSupport_error_length = 0xc4;

/*
 * error SignatureExpired()
 *   - Defined in SignedZoneEventsAndErrors.sol
 * Memory layout:
 *   - 0x00: Left-padded selector (data begins at 0x1c)
 *   - 0x20: expiration
 *   - 0x40: orderHash
 * Revert buffer is memory[0x1c:0x60]
 */
uint256 constant SignatureExpired_error_selector = 0x16546071;
uint256 constant SignatureExpired_error_expiration_ptr = 0x20;
uint256 constant SignatureExpired_error_orderHash_ptr = 0x40;
uint256 constant SignatureExpired_error_length = 0x44;

// Zone parameter calldata pointers
uint256 constant Zone_parameters_cdPtr = 0x04;
uint256 constant Zone_parameters_fulfiller_cdPtr = 0x44;
uint256 constant Zone_consideration_head_cdPtr = 0xa4;
uint256 constant Zone_extraData_cdPtr = 0xc4;

// Zone parameter memory pointers
uint256 constant Zone_parameters_ptr = 0x20;

// Zone parameter offsets
uint256 constant Zone_parameters_offset = 0x24;
uint256 constant expectedFulfiller_offset = 0x45;
uint256 constant actualConsideration_offset = 0x84;
uint256 constant expectedConsideration_offset = 0xa2;

File 120 of 129 : SignedZone.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {
    ZoneParameters,
    Schema,
    ReceivedItem
} from "../lib/ConsiderationStructs.sol";

import { ZoneInterface } from "../interfaces/ZoneInterface.sol";

import {
    SignedZoneEventsAndErrors
} from "./interfaces/SignedZoneEventsAndErrors.sol";

import { SIP5Interface } from "./interfaces/SIP5Interface.sol";

import {
    SignedZoneControllerInterface
} from "./interfaces/SignedZoneControllerInterface.sol";

import "./lib/SignedZoneConstants.sol";

/**
 * @title  SignedZone
 * @author ryanio, BCLeFevre
 * @custom:modifiedby Tony Snark
 * @notice SignedZone is an implementation of SIP-7 that requires orders
 *         to be signed by an approved signer.
 *         https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md
 *
 *         Modification:
 *         Removes support for SIP7 sub-standard 1.
 *         Adds support for SIP7 sub-standard 3.
 */
contract SignedZone is SignedZoneEventsAndErrors, ZoneInterface, SIP5Interface {
    /// @dev The zone's controller that is set during deployment.
    address private immutable _controller;

    /// @dev The authorized signers, and if they are active
    mapping(address => bool) private _signers;

    /// @dev The EIP-712 digest parameters.
    bytes32 internal immutable _NAME_HASH = keccak256(bytes("SignedZone"));
    bytes32 internal immutable _VERSION_HASH = keccak256(bytes("1.0.0"));
    // prettier-ignore
    bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH = keccak256(
          abi.encodePacked(
            "EIP712Domain(",
                "string name,",
                "string version,",
                "uint256 chainId,",
                "address verifyingContract",
            ")"
          )
        );
    // prettier-ignore
    bytes32 internal immutable _SIGNED_ORDER_TYPEHASH = keccak256(
          abi.encodePacked(
            "SignedOrder(",
                "address fulfiller,",
                "uint64 expiration,",
                "bytes32 orderHash,",
                "bytes context",
            ")"
          )
        );

    bytes public constant CONSIDERATION_BYTES =
        // prettier-ignore
        abi.encodePacked(
              "Consideration(",
                  "ReceivedItem[] consideration",
              ")"
        );

    bytes public constant RECEIVED_ITEM_BYTES =
        // prettier-ignore
        abi.encodePacked(
              "ReceivedItem(",
                  "uint8 itemType,",
                  "address token,",
                  "uint256 identifier,",
                  "uint256 amount,",
                  "address recipient",
              ")"
        );

    bytes32 public constant RECEIVED_ITEM_HASHTYPE =
        keccak256(RECEIVED_ITEM_BYTES);

    bytes32 public constant CONSIDERATION_HASHTYPE =
        keccak256(abi.encodePacked(CONSIDERATION_BYTES, RECEIVED_ITEM_BYTES));

    uint256 internal immutable _CHAIN_ID = block.chainid;
    bytes32 internal immutable _DOMAIN_SEPARATOR;

    /**
     * @notice Constructor to deploy the contract.
     */
    constructor() {
        // Set the deployer as the controller.
        _controller = msg.sender;

        // Derive and set the domain separator.
        _DOMAIN_SEPARATOR = _deriveDomainSeparator();

        // Emit an event to signal a SIP-5 contract has been deployed.
        emit SeaportCompatibleContractDeployed();
    }

    /**
     * @notice Check if a given order including extraData is currently valid.
     *
     * @dev This function is called by Seaport whenever any extraData is
     *      provided by the caller.
     *
     * @return validOrderMagicValue A magic value indicating if the order is
     *                              currently valid.
     */
    function validateOrder(ZoneParameters calldata zoneParameters)
        public
        view
        virtual
        override
        returns (bytes4 validOrderMagicValue)
    {
        // Check Zone parameters validity.
        _assertValidZoneParameters();

        // Put the extraData and orderHash on the stack for cheaper access.
        bytes calldata extraData = zoneParameters.extraData;
        bytes32 orderHash = zoneParameters.orderHash;
        uint256 considerationLength;
        // Declare a variable to hold the expiration.
        uint64 expiration;

        // Validate the extraData.
        assembly {
            // Get the length of the extraData.
            let extraDataPtr := add(0x24, calldataload(Zone_extraData_cdPtr))
            let extraDataLength := calldataload(extraDataPtr)

            if iszero(
                eq(extraDataLength, InvalidExtraDataLength_epected_length)
            ) {
                // Store left-padded selector with push4, mem[28:32] = selector
                mstore(0, InvalidExtraDataLength_error_selector)
                mstore(InvalidExtraDataLength_error_orderHash_ptr, orderHash)
                // revert(abi.encodeWithSignature(
                //   "InvalidExtraDataLength(bytes32)", orderHash)
                // )
                revert(0x1c, InvalidExtraDataLength_error_length)
            }

            // extraData bytes 0-1: SIP-6 version byte (MUST be 0x00)
            let versionByte := shr(248, calldataload(add(extraDataPtr, 0x20)))

            if iszero(eq(versionByte, 0x00)) {
                // Store left-padded selector with push4, mem[28:32] = selector
                mstore(0, InvalidSIP6Version_error_selector)
                mstore(InvalidSIP6Version_error_orderHash_ptr, orderHash)
                // revert(abi.encodeWithSignature(
                //   "InvalidSIP6Version(bytes32)", orderHash)
                // )
                revert(0x1c, InvalidSIP6Version_error_length)
            }

            // extraData bytes 93-94: Substandard #1 (MUST be 0x00)
            let subStandardVersionByte := shr(
                248,
                calldataload(
                    add(extraDataPtr, ExtraData_substandard_version_byte_offset)
                )
            )

            if iszero(eq(subStandardVersionByte, 0x00)) {
                // Store left-padded selector with push4, mem[28:32] = selector
                mstore(0, InvalidSubstandardVersion_error_selector)
                mstore(InvalidSubstandardVersion_error_orderHash_ptr, orderHash)
                // revert(abi.encodeWithSignature(
                //   "InvalidSubstandardVersion(bytes32)", orderHash)
                // )
                revert(0x1c, InvalidSubstandardVersion_error_length)
            }

            // extraData bytes 21-29: expiration timestamp (uint64)
            expiration := shr(
                192,
                calldataload(add(extraDataPtr, ExtraData_expiration_offset))
            )
            // Revert if expired.
            if lt(expiration, timestamp()) {
                // Store left-padded selector with push4, mem[28:32] = selector
                mstore(0, SignatureExpired_error_selector)
                mstore(SignatureExpired_error_expiration_ptr, expiration)
                mstore(SignatureExpired_error_orderHash_ptr, orderHash)
                // revert(abi.encodeWithSignature(
                //   "SignatureExpired(uint256, bytes32)", expiration orderHash)
                // )
                revert(0x1c, SignatureExpired_error_length)
            }

            // // Get the length of the consideration array.
            considerationLength := calldataload(
                add(0x24, calldataload(Zone_consideration_head_cdPtr))
            )
        }

        // extraData bytes 29-93: signature
        // (strictly requires 64 byte compact sig, EIP-2098)
        bytes calldata signature = extraData[29:93];

        // extraData bytes 93-end: context (optional, variable length)
        bytes calldata context = extraData[93:];

        // Check the validity of the Substandard #1 extraData and get the
        // expected fulfiller address.
        address expectedFulfiller = _getExpectedFulfiller(orderHash);

        // Check the validity of the Substandard #1 extraData and get the
        // expected fulfiller address.
        if (considerationLength > 0) {
            _assertValidSubstandard(
                _deriveConsiderationHash(zoneParameters.consideration),
                orderHash
            );
        }

        // Derive the signedOrder hash.
        bytes32 signedOrderHash = _deriveSignedOrderHash(
            expectedFulfiller,
            expiration,
            orderHash,
            context
        );

        // Derive the EIP-712 digest using the domain separator and signedOrder
        // hash.
        bytes32 digest = _deriveEIP712Digest(
            _domainSeparator(),
            signedOrderHash
        );

        // Recover the signer address from the digest and signature.
        address recoveredSigner = _recoverSigner(digest, signature);

        // Revert if the signer is not active.
        if (!_signers[recoveredSigner]) {
            revert SignerNotActive(recoveredSigner, orderHash);
        }
        // Return the selector of validateOrder as the magic value.
        validOrderMagicValue = ZoneInterface.validateOrder.selector;
    }

    /**
     * @dev Returns Seaport metadata for this contract, returning the
     *      contract name and supported schemas.
     *
     * @return name The contract name
     * @return schemas  The supported SIPs
     */
    function getSeaportMetadata()
        external
        view
        override(SIP5Interface, ZoneInterface)
        returns (string memory name, Schema[] memory schemas)
    {
        // Return the supported SIPs.
        schemas = new Schema[](1);
        schemas[0].id = 7;

        // Get the SIP-7 information.
        (
            bytes32 domainSeparator,
            string memory zoneName,
            string memory apiEndpoint,
            uint256[] memory substandards,
            string memory documentationURI
        ) = _sip7Information();

        // Return the zone name.
        name = zoneName;

        // Encode the SIP-7 information.
        schemas[0].metadata = abi.encode(
            domainSeparator,
            apiEndpoint,
            substandards,
            documentationURI
        );
    }

    /**
     * @notice The fallback function is used as a dispatcher for the
     *         `updateSigner`, `getActiveSigners` and `supportsInterface`
     *         functions.
     */
    // prettier-ignore
    fallback(bytes calldata) external payable returns (bytes memory output) {
        // Get the function selector.
        bytes4 selector = msg.sig;

        if (selector == 0xf460590b) {
            // updateSigner(address,bool)

            // Get the signer, and active status.
            address signer = abi.decode(msg.data[4:], (address));
            bool active = abi.decode(msg.data[36:], (bool));

            // Call to update the signer.
            _updateSigner(signer, active);
        } else if (selector == 0xa784b80c) {
            // getActiveSigners()

            // Call the internal function to get the active signers.
            return abi.encode(_getActiveSigners());
        } else if (selector == 0x01ffc9a7) {
            // supportsInterface(bytes4)

            // Get the interface ID.
            bytes4 interfaceId = abi.decode(msg.data[4:], (bytes4));

            // Call the internal function to determine if the interface is
            // supported.
            return abi.encode(_supportsInterface(interfaceId));
        }
    }

    /**
     * @notice Add or remove a signer to the zone.
     *         Only the controller can call this function.
     *
     * @param signer The signer address to add or remove.
     */
    function _updateSigner(address signer, bool active) internal {
        // Only the controller can call this function.
        _assertCallerIsController();
        // Add or remove the signer.
        active ? _addSigner(signer) : _removeSigner(signer);
    }

    /**
     * @notice Add a new signer to the zone.
     *         Only the controller or an active signer can call this function.
     *
     * @param signer The new signer address to add.
     */
    function _addSigner(address signer) internal {
        // Set the signer info.
        _signers[signer] = true;
        // Emit an event that the signer was added.
        emit SignerAdded(signer);
    }

    /**
     * @notice Remove an active signer from the zone.
     *         Only the controller or an active signer can call this function.
     *
     * @param signer The signer address to remove.
     */
    function _removeSigner(address signer) internal {
        // Set the signer's active status to false.
        _signers[signer] = false;

        // Emit an event that the signer was removed.
        emit SignerRemoved(signer);
    }

    /**
     * @notice Returns the active signers for the zone.
     *
     * @return signers The active signers.
     */
    function _getActiveSigners()
        internal
        view
        returns (address[] memory signers)
    {
        // Return the active signers for the zone by calling the controller.
        signers = SignedZoneControllerInterface(_controller).getActiveSigners(
            address(this)
        );
    }

    /**
     * @notice Returns whether the interface is supported.
     *
     * @param interfaceId The interface id to check against.
     */
    function _supportsInterface(bytes4 interfaceId)
        internal
        pure
        returns (bool supportsInterface)
    {
        // Determine if the interface is supported.
        supportsInterface =
            interfaceId == type(SIP5Interface).interfaceId || // SIP-5
            interfaceId == type(ZoneInterface).interfaceId || // ZoneInterface
            interfaceId == 0x01ffc9a7; // ERC-165
    }

    /**
     * @notice Internal call to return the signing information, substandards,
     *         and documentation about the zone.
     *
     * @return domainSeparator  The domain separator used for signing.
     * @return zoneName         The zone name.
     * @return apiEndpoint      The API endpoint for the zone.
     * @return substandards     The substandards supported by the zone.
     * @return documentationURI The documentation URI for the zone.
     */
    function _sip7Information()
        internal
        view
        returns (
            bytes32 domainSeparator,
            string memory zoneName,
            string memory apiEndpoint,
            uint256[] memory substandards,
            string memory documentationURI
        )
    {
        // Return the SIP-7 information.
        domainSeparator = _domainSeparator();

        // Get the SIP-7 information from the controller.
        (
            ,
            zoneName,
            apiEndpoint,
            substandards,
            documentationURI
        ) = SignedZoneControllerInterface(_controller)
            .getAdditionalZoneInformation(address(this));
    }

    /**
     * @dev Derive the signedOrder hash from the orderHash and expiration.
     *
     * @param fulfiller  The expected fulfiller address.
     * @param expiration The signature expiration timestamp.
     * @param orderHash  The order hash.
     * @param context    The optional variable-length context.
     *
     * @return signedOrderHash The signedOrder hash.
     *
     */
    function _deriveSignedOrderHash(
        address fulfiller,
        uint64 expiration,
        bytes32 orderHash,
        bytes calldata context
    ) internal view returns (bytes32 signedOrderHash) {
        // Derive the signed order hash.
        signedOrderHash = keccak256(
            abi.encode(
                _SIGNED_ORDER_TYPEHASH,
                fulfiller,
                expiration,
                orderHash,
                keccak256(context)
            )
        );
    }

    /**
     * @dev Internal view function to return the signer of a signature.
     *
     * @param digest    The digest to verify the signature against.
     * @param signature A signature from the signer indicating that the order
     *                  has been approved.
     *
     * @return recoveredSigner The recovered signer.
     */
    function _recoverSigner(bytes32 digest, bytes memory signature)
        internal
        view
        returns (address recoveredSigner)
    {
        // Utilize assembly to perform optimized signature verification check.
        assembly {
            // Ensure that first word of scratch space is empty.
            mstore(0, 0)

            // Declare value for v signature parameter.
            let v

            // Get the length of the signature.
            let signatureLength := mload(signature)

            // Get the pointer to the value preceding the signature length.
            // This will be used for temporary memory overrides - either the
            // signature head for isValidSignature or the digest for ecrecover.
            let wordBeforeSignaturePtr := sub(signature, OneWord)

            // Cache the current value behind the signature to restore it later.
            let cachedWordBeforeSignature := mload(wordBeforeSignaturePtr)

            // Declare lenDiff + recoveredSigner scope to manage stack pressure.
            {
                // Take the difference between the max ECDSA signature length
                // and the actual signature length. Overflow desired for any
                // values > 65. If the diff is not 0 or 1, it is not a valid
                // ECDSA signature - move on to EIP1271 check.
                let lenDiff := sub(ECDSA_MaxLength, signatureLength)

                // If diff is 0 or 1, it may be an ECDSA signature.
                // Try to recover signer.
                if iszero(gt(lenDiff, 1)) {
                    // Read the signature `s` value.
                    let originalSignatureS := mload(
                        add(signature, ECDSA_signature_s_offset)
                    )

                    // Read the first byte of the word after `s`. If the
                    // signature is 65 bytes, this will be the real `v` value.
                    // If not, it will need to be modified - doing it this way
                    // saves an extra condition.
                    v := byte(
                        0,
                        mload(add(signature, ECDSA_signature_v_offset))
                    )

                    // If lenDiff is 1, parse 64-byte signature as ECDSA.
                    if lenDiff {
                        // Extract yParity from highest bit of vs and add 27 to
                        // get v.
                        v := add(
                            shr(MaxUint8, originalSignatureS),
                            Signature_lower_v
                        )

                        // Extract canonical s from vs, all but the highest bit.
                        // Temporarily overwrite the original `s` value in the
                        // signature.
                        mstore(
                            add(signature, ECDSA_signature_s_offset),
                            and(
                                originalSignatureS,
                                EIP2098_allButHighestBitMask
                            )
                        )
                    }
                    // Temporarily overwrite the signature length with `v` to
                    // conform to the expected input for ecrecover.
                    mstore(signature, v)

                    // Temporarily overwrite the word before the length with
                    // `digest` to conform to the expected input for ecrecover.
                    mstore(wordBeforeSignaturePtr, digest)

                    // Attempt to recover the signer for the given signature. Do
                    // not check the call status as ecrecover will return a null
                    // address if the signature is invalid.
                    pop(
                        staticcall(
                            gas(),
                            Ecrecover_precompile, // Call ecrecover precompile.
                            wordBeforeSignaturePtr, // Use data memory location.
                            Ecrecover_args_size, // Size of digest, v, r, and s.
                            0, // Write result to scratch space.
                            OneWord // Provide size of returned result.
                        )
                    )

                    // Restore cached word before signature.
                    mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature)

                    // Restore cached signature length.
                    mstore(signature, signatureLength)

                    // Restore cached signature `s` value.
                    mstore(
                        add(signature, ECDSA_signature_s_offset),
                        originalSignatureS
                    )

                    // Read the recovered signer from the buffer given as return
                    // space for ecrecover.
                    recoveredSigner := mload(0)
                }
            }

            // Restore the cached values overwritten by selector, digest and
            // signature head.
            mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature)
        }
    }

    /**
     * @dev Internal view function to get the EIP-712 domain separator. If the
     *      chainId matches the chainId set on deployment, the cached domain
     *      separator will be returned; otherwise, it will be derived from
     *      scratch.
     *
     * @return The domain separator.
     */
    function _domainSeparator() internal view returns (bytes32) {
        // prettier-ignore
        return block.chainid == _CHAIN_ID
            ? _DOMAIN_SEPARATOR
            : _deriveDomainSeparator();
    }

    /**
     * @dev Internal view function to derive the EIP-712 domain separator.
     *
     * @return domainSeparator The derived domain separator.
     */
    function _deriveDomainSeparator()
        internal
        view
        returns (bytes32 domainSeparator)
    {
        bytes32 typehash = _EIP_712_DOMAIN_TYPEHASH;
        bytes32 nameHash = _NAME_HASH;
        bytes32 versionHash = _VERSION_HASH;

        // Leverage scratch space and other memory to perform an efficient hash.
        assembly {
            // Retrieve the free memory pointer; it will be replaced afterwards.
            let freeMemoryPointer := mload(FreeMemoryPointerSlot)

            // Retrieve value at 0x80; it will also be replaced afterwards.
            let slot0x80 := mload(Slot0x80)

            // Place typehash, name hash, and version hash at start of memory.
            mstore(0, typehash)
            mstore(OneWord, nameHash)
            mstore(TwoWords, versionHash)

            // Place chainId in the next memory location.
            mstore(ThreeWords, chainid())

            // Place the address of this contract in the next memory location.
            mstore(FourWords, address())

            // Hash relevant region of memory to derive the domain separator.
            domainSeparator := keccak256(0, FiveWords)

            // Restore the free memory pointer.
            mstore(FreeMemoryPointerSlot, freeMemoryPointer)

            // Restore the zero slot to zero.
            mstore(ZeroSlot, 0)

            // Restore the value at 0x80.
            mstore(Slot0x80, slot0x80)
        }
    }

    /**
     * @dev Internal pure function to efficiently derive an digest to sign for
     *      an order in accordance with EIP-712.
     *
     * @param domainSeparator The domain separator.
     * @param signedOrderHash The signedOrder hash.
     *
     * @return digest The digest hash.
     */
    function _deriveEIP712Digest(
        bytes32 domainSeparator,
        bytes32 signedOrderHash
    ) internal pure returns (bytes32 digest) {
        // Leverage scratch space to perform an efficient hash.
        assembly {
            // Place the EIP-712 prefix at the start of scratch space.
            mstore(0, EIP_712_PREFIX)

            // Place the domain separator in the next region of scratch space.
            mstore(EIP712_DomainSeparator_offset, domainSeparator)

            // Place the signed order hash in scratch space, spilling into the
            // first two bytes of the free memory pointer — this should never be
            // set as memory cannot be expanded to that size, and will be
            // zeroed out after the hash is performed.
            mstore(EIP712_SignedOrderHash_offset, signedOrderHash)

            // Hash the relevant region
            digest := keccak256(0, EIP712_DigestPayload_size)

            // Clear out the dirtied bits in the memory pointer.
            mstore(EIP712_SignedOrderHash_offset, 0)
        }
    }

    /**
     * @dev Private view function to revert if the caller is not the
     *      controller.
     */
    function _assertCallerIsController() internal view {
        // Get the controller address to use in the assembly block.
        address controller = _controller;

        assembly {
            // Revert if the caller is not the controller.
            if iszero(eq(caller(), controller)) {
                // Store left-padded selector with push4, mem[28:32] = selector
                mstore(0, InvalidController_error_selector)
                // revert(abi.encodeWithSignature(
                //   "InvalidController()")
                // )
                revert(0x1c, InvalidController_error_length)
            }
        }
    }

    /**
     * @dev Internal pure function to validate calldata offsets for the
     *      dyanamic type in ZoneParameters. This ensures that functions using
     *      the calldata object normally will be using the same data as the
     *      assembly functions and that values that are bound to a given range
     *      are within that range.
     */
    function _assertValidZoneParameters() internal pure {
        // Utilize assembly in order to read offset data directly from calldata.
        assembly {
            /*
             * Checks:
             * 1. Zone parameters struct offset == 0x20
             */

            // Zone parameters at calldata 0x04 must have offset of 0x20.
            if iszero(
                eq(calldataload(Zone_parameters_cdPtr), Zone_parameters_ptr)
            ) {
                // Store left-padded selector with push4 (reduces bytecode), mem[28:32] = selector
                mstore(0, InvalidZoneParameterEncoding_error_selector)
                // revert(abi.encodeWithSignature("InvalidZoneParameterEncoding()"))
                revert(0x1c, InvalidZoneParameterEncoding_error_length)
            }
        }
    }

    /**
     * @dev Internal pure function to ensure that the context argument for the
     *      supplied extra data follows the substandard #1 format. Returns the
     *      expected fulfiller of the order for deriving the signed order hash.
     *
     * @param orderHash The order hash.
     *
     * @return expectedFulfiller The expected fulfiller of the order.
     */
    function _getExpectedFulfiller(bytes32 orderHash)
        internal
        pure
        returns (address expectedFulfiller)
    {
        // Revert if the expected fulfiller is not the zero address and does
        // not match the actual fulfiller
        assembly {
            // Get the actual fulfiller.
            let actualFulfiller := calldataload(Zone_parameters_fulfiller_cdPtr)
            let extraDataPtr := calldataload(Zone_extraData_cdPtr)

            // Get the expected fulfiller.
            expectedFulfiller := shr(
                96,
                calldataload(add(expectedFulfiller_offset, extraDataPtr))
            )

            // Revert if expected fulfiller is not the zero address and does
            // not match the actual fulfiller.
            if and(
                iszero(iszero(expectedFulfiller)),
                iszero(eq(expectedFulfiller, actualFulfiller))
            ) {
                // Store left-padded selector with push4, mem[28:32] = selector
                mstore(0, InvalidFulfiller_error_selector)
                mstore(
                    InvalidFulfiller_error_expectedFulfiller_ptr,
                    expectedFulfiller
                )
                mstore(
                    InvalidFulfiller_error_actualFulfiller_ptr,
                    actualFulfiller
                )
                mstore(InvalidFulfiller_error_orderHash_ptr, orderHash)
                // revert(abi.encodeWithSignature(
                //   "InvalidFulfiller(address,address,bytes32)", expectedFulfiller, actualFulfiller, orderHash)
                // )
                revert(0x1c, InvalidFulfiller_error_length)
            }
        }
    }

    /**
     * @dev Internal pure function to ensure that the context argument for the
     *      supplied extra data follows the substandard #1 format. Returns the
     *      expected fulfiller of the order for deriving the signed order hash.
     *
     */
    function _assertValidSubstandard(
        bytes32 considerationHash,
        bytes32 orderHash
    ) internal pure {
        // identifier does not match the actual consideration.
        assembly {
            let extraDataPtr := calldataload(Zone_extraData_cdPtr)
            let considerationPtr := calldataload(Zone_consideration_head_cdPtr)
            // Get the actual consideration.
            let actualConsideration := calldataload(
                add(actualConsideration_offset, considerationPtr)
            )

            // Get the expected consideration.
            let expectedConsiderationHash := calldataload(
                add(expectedConsideration_offset, extraDataPtr) //TODO rename
            )
            // Revert if expected consideration item does not match the actual
            // consideration item.
            if iszero(eq(considerationHash, expectedConsiderationHash)) {
                // Store left-padded selector with push4, mem[28:32] = selector
                mstore(0, InvalidConsideration_error_selector)
                mstore(
                    InvalidConsideration_error_expectedConsideration_ptr,
                    expectedConsiderationHash
                )
                mstore(
                    InvalidConsideration_error_actualConsideration_ptr,
                    actualConsideration
                )
                mstore(InvalidConsideration_error_orderHash_ptr, orderHash)
                // revert(abi.encodeWithSignature(
                //   "InvalidConsideration(uint256,uint256,bytes32)", expectedConsideration, actualConsideration, orderHash)
                // )
                revert(0x1c, InvalidConsideration_error_length)
            }
        }
    }

    /// @dev Calculates consideration hash
    function _deriveConsiderationHash(ReceivedItem[] calldata consideration)
        internal
        pure
        returns (bytes32)
    {
        uint256 numberOfItems = consideration.length;
        bytes32[] memory considerationHashes = new bytes32[](numberOfItems);
        for (uint256 i; i < numberOfItems; ) {
            considerationHashes[i] = _deriveReceivedItemHash(consideration[i]);
            unchecked {
                ++i;
            }
        }
        return
            keccak256(
                abi.encode(
                    CONSIDERATION_HASHTYPE,
                    keccak256(abi.encodePacked(considerationHashes))
                )
            );
    }

    /// @dev Calculates consideration item hash
    function _deriveReceivedItemHash(ReceivedItem calldata receivedItem)
        internal
        pure
        returns (bytes32)
    {
        return
            keccak256(
                abi.encode(
                    RECEIVED_ITEM_HASHTYPE,
                    receivedItem.itemType,
                    receivedItem.token,
                    receivedItem.identifier,
                    receivedItem.amount,
                    receivedItem.recipient
                )
            );
    }
}

File 121 of 129 : SignedZoneController.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { SignedZone } from "./SignedZone.sol";

import { SignedZoneInterface } from "./interfaces/SignedZoneInterface.sol";

import {
    SignedZoneControllerInterface
} from "./interfaces/SignedZoneControllerInterface.sol";

import {
    SignedZoneControllerEventsAndErrors
} from "./interfaces/SignedZoneControllerEventsAndErrors.sol";

import "./lib/SignedZoneConstants.sol";

/**
 * @title  SignedZoneController
 * @author BCLeFevre
 * @notice SignedZoneController enables the deploying of SignedZones.
 *         SignedZones are an implementation of SIP-7 that requires orders to
 *         be signed by  an approved signer.
 *         https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md
 */
contract SignedZoneController is
    SignedZoneControllerInterface,
    SignedZoneControllerEventsAndErrors
{
    /**
     * @dev The struct for storing signer info.
     */
    struct SignerInfo {
        /// @dev If the signer is currently active.
        bool active;
        /// @dev If the signer has been active before.
        bool previouslyActive;
    }

    // Properties used by the signed zone, stored on the controller.
    struct SignedZoneProperties {
        /// @dev Owner of the signed zone (used for permissioned functions)
        address owner;
        /// @dev Potential owner of the signed zone
        address potentialOwner;
        /// @dev The name for this zone returned in getSeaportMetadata().
        string zoneName;
        /// @dev The API endpoint where orders for this zone can be signed.
        ///      Request and response payloads are defined in SIP-7.
        string apiEndpoint;
        /// @dev The URI to the documentation describing the behavior of the
        ///      contract.
        string documentationURI;
        /// @dev The substandards supported by this zone.
        ///      Substandards are defined in SIP-7.
        uint256[] substandards;
        /// @dev Mapping of signer information keyed by signer Address
        mapping(address => SignerInfo) signers;
        /// @dev List of active signers
        address[] activeSignerList;
    }

    /// @dev Mapping of signed zone properties keyed by the Signed Zone
    ///      address.
    mapping(address => SignedZoneProperties) internal _signedZones;

    /// @dev The EIP-712 digest parameters for the SignedZone.
    bytes32 internal immutable _NAME_HASH = keccak256(bytes("SignedZone"));
    bytes32 internal immutable _VERSION_HASH = keccak256(bytes("1.0"));
    // prettier-ignore
    bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH = keccak256(
          abi.encodePacked(
            "EIP712Domain(",
                "string name,",
                "string version,",
                "uint256 chainId,",
                "address verifyingContract",
            ")"
          )
        );
    uint256 internal immutable _CHAIN_ID = block.chainid;

    // Set the signed zone creation code as an immutable argument.
    bytes32 internal immutable _SIGNED_ZONE_CREATION_CODE_HASH;

    /**
     * @dev Initialize contract
     */
    constructor() {
        // Derive the signed zone creation code hash and set it as an
        // immutable.
        _SIGNED_ZONE_CREATION_CODE_HASH = keccak256(
            type(SignedZone).creationCode
        );
    }

    /**
     * @notice Deploy a SignedZone to a precomputed address.
     *
     * @param zoneName    The name for the zone returned in
     *                    getSeaportMetadata().
     * @param apiEndpoint The API endpoint where orders for this zone can be
     *                    signed.
     * @param documentationURI The URI to the documentation describing the
     *                         behavior of the contract.
     *                    Request and response payloads are defined in SIP-7.
     * @param salt        The salt to be used to derive the zone address
     * @param initialOwner The initial owner to set for the new zone.
     *
     * @return derivedAddress The derived address for the zone.
     */
    function createZone(
        string memory zoneName,
        string memory apiEndpoint,
        string memory documentationURI,
        address initialOwner,
        bytes32 salt
    ) external override returns (address derivedAddress) {
        // Ensure that an initial owner has been supplied.
        if (initialOwner == address(0)) {
            revert InvalidInitialOwner();
        }

        // Ensure the first 20 bytes of the salt are the same as the msg.sender.
        if ((address(uint160(bytes20(salt))) != msg.sender)) {
            // Revert with an error indicating that the creator is invalid.
            revert InvalidCreator();
        }

        // Derive the SignedZone address from the deployer, salt and creation
        // code hash.
        derivedAddress = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            bytes1(0xff),
                            address(this),
                            salt,
                            _SIGNED_ZONE_CREATION_CODE_HASH
                        )
                    )
                )
            )
        );

        // TODO : Check runtime code hash to ensure that the zone is not already
        // deployed.
        // Revert if a zone is currently deployed to the derived address.
        if (derivedAddress.code.length != 0) {
            revert ZoneAlreadyExists(derivedAddress);
        }

        // Deploy the zone using the supplied salt.
        new SignedZone{ salt: salt }();

        // Initialize storage variable referencing signed zone properties.
        SignedZoneProperties storage signedZoneProperties = _signedZones[
            derivedAddress
        ];

        // Set the supplied intial owner as the owner of the zone.
        signedZoneProperties.owner = initialOwner;
        // Set the zone name.
        signedZoneProperties.zoneName = zoneName;
        // Set the API endpoint.
        signedZoneProperties.apiEndpoint = apiEndpoint;
        // Set the documentation URI.
        signedZoneProperties.documentationURI = documentationURI;
        // Set the substandard.
        signedZoneProperties.substandards = [3];

        // Emit an event signifying that the zone was created.
        emit ZoneCreated(
            derivedAddress,
            zoneName,
            apiEndpoint,
            documentationURI,
            salt
        );

        // Emit an event indicating that zone ownership has been assigned.
        emit OwnershipTransferred(derivedAddress, address(0), initialOwner);
    }

    /**
     * @notice Initiate zone ownership transfer by assigning a new potential
     *         owner for the given zone. Once set, the new potential owner
     *         may call `acceptOwnership` to claim ownership of the zone.
     *         Only the owner of the zone in question may call this function.
     *
     * @param zone The zone for which to initiate ownership transfer.
     * @param newPotentialOwner The new potential owner of the zone.
     */
    function transferOwnership(address zone, address newPotentialOwner)
        external
        override
    {
        // Ensure the caller is the current owner of the zone in question.
        _assertCallerIsZoneOwner(zone);

        // Ensure the new potential owner is not an invalid address.
        if (newPotentialOwner == address(0)) {
            revert NewPotentialOwnerIsZeroAddress(zone);
        }

        // Ensure the new potential owner is not already set.
        if (newPotentialOwner == _signedZones[zone].potentialOwner) {
            revert NewPotentialOwnerAlreadySet(zone, newPotentialOwner);
        }

        // Emit an event indicating that the potential owner has been updated.
        emit PotentialOwnerUpdated(newPotentialOwner);

        // Set the new potential owner as the potential owner of the zone.
        _signedZones[zone].potentialOwner = newPotentialOwner;
    }

    /**
     * @notice Clear the currently set potential owner, if any, from a zone.
     *         Only the owner of the zone in question may call this function.
     *
     * @param zone The zone for which to cancel ownership transfer.
     */
    function cancelOwnershipTransfer(address zone) external override {
        // Ensure the caller is the current owner of the zone in question.
        _assertCallerIsZoneOwner(zone);

        // Ensure that ownership transfer is currently possible.
        if (_signedZones[zone].potentialOwner == address(0)) {
            revert NoPotentialOwnerCurrentlySet(zone);
        }

        // Emit an event indicating that the potential owner has been cleared.
        emit PotentialOwnerUpdated(address(0));

        // Clear the current new potential owner from the zone.
        _signedZones[zone].potentialOwner = address(0);
    }

    /**
     * @notice Accept ownership of a supplied zone. Only accounts that the
     *         current owner has set as the new potential owner may call this
     *         function.
     *
     * @param zone The zone for which to accept ownership.
     */
    function acceptOwnership(address zone) external override {
        // Ensure that the zone in question exists.
        _assertZoneExists(zone);

        // If caller does not match current potential owner of the zone...
        if (msg.sender != _signedZones[zone].potentialOwner) {
            // Revert, indicating that caller is not current potential owner.
            revert CallerIsNotNewPotentialOwner(zone);
        }

        // Emit an event indicating that the potential owner has been cleared.
        emit PotentialOwnerUpdated(address(0));

        // Clear the current new potential owner from the zone.
        _signedZones[zone].potentialOwner = address(0);

        // Emit an event indicating zone ownership has been transferred.
        emit OwnershipTransferred(zone, _signedZones[zone].owner, msg.sender);

        // Set the caller as the owner of the zone.
        _signedZones[zone].owner = msg.sender;
    }

    /**
     * @notice Retrieve the current owner of a deployed zone.
     *
     * @param zone The zone for which to retrieve the associated owner.
     *
     * @return owner The owner of the supplied zone.
     */
    function ownerOf(address zone)
        external
        view
        override
        returns (address owner)
    {
        // Ensure that the zone in question exists.
        _assertZoneExists(zone);

        // Retrieve the current owner of the zone in question.
        owner = _signedZones[zone].owner;
    }

    /**
     * @notice Retrieve the potential owner, if any, for a given zone. The
     *         current owner may set a new potential owner via
     *         `transferOwnership` and that owner may then accept ownership of
     *         the zone in question via `acceptOwnership`.
     *
     * @param zone The zone for which to retrieve the potential owner.
     *
     * @return potentialOwner The potential owner, if any, for the zone.
     */
    function getPotentialOwner(address zone)
        external
        view
        override
        returns (address potentialOwner)
    {
        // Ensure that the zone in question exists.
        _assertZoneExists(zone);

        // Retrieve the current potential owner of the zone in question.
        potentialOwner = _signedZones[zone].potentialOwner;
    }

    /**
     * @notice Returns the active signers for the zone.
     *
     * @param zone The zone to return the active signers for.
     *
     * @return signers The active signers.
     */
    function getActiveSigners(address zone)
        external
        view
        override
        returns (address[] memory signers)
    {
        // Ensure that the zone in question exists.
        _assertZoneExists(zone);

        // Retrieve storage region where the singers for the signedZone are
        // stored.
        SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

        // Return the active signers for the zone.
        signers = signedZoneProperties.activeSignerList;
    }

    /**
     * @notice Update the API endpoint returned by a zone.
     *         Only the owner or an active signer of the supplied zone can call
     *         this function.
     *
     * @param zone     The signed zone to update the API endpoint for.
     * @param newApiEndpoint The new API endpoint.
     */
    function updateAPIEndpoint(address zone, string calldata newApiEndpoint)
        external
        override
    {
        // Ensure the caller is the owner or an active signer of the signed zone.
        _assertCallerIsZoneOwnerOrSigner(zone);

        // Retrieve storage region where the singers for the signedZone are
        // stored.
        SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

        // Update the API endpoint on the signed zone.
        signedZoneProperties.apiEndpoint = newApiEndpoint;
    }

    /**
     * @notice Add or remove a signer from the supplied zone.
     *         Only the owner or an active signer of the supplied zone can call
     *         this function.
     *
     * @param zone     The signed zone to update the signer permissions for.
     * @param signer   The signer to update the permissions for.
     * @param active   Whether the signer should be active or not.
     */
    function updateSigner(
        address zone,
        address signer,
        bool active
    ) external override {
        // Ensure the caller is the owner or an active signer of the signed zone.
        _assertCallerIsZoneOwnerOrSigner(zone);

        // Retrieve storage region where the singers for the signedZone are
        // stored.
        SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

        // Validate signer permissions.
        _assertSignerPermissions(signedZoneProperties, signer, active);

        // Update the signer on the signed zone.
        SignedZoneInterface(zone).updateSigner(signer, active);

        // Update the signer information.
        signedZoneProperties.signers[signer].active = active;
        signedZoneProperties.signers[signer].previouslyActive = true;
        // Add the signer to the list of signers if they are active.
        if (active) {
            signedZoneProperties.activeSignerList.push(signer);
        } else {
            // Remove the signer from the list of signers.
            for (
                uint256 i = 0;
                i < signedZoneProperties.activeSignerList.length;

            ) {
                if (signedZoneProperties.activeSignerList[i] == signer) {
                    signedZoneProperties.activeSignerList[
                            i
                        ] = signedZoneProperties.activeSignerList[
                        signedZoneProperties.activeSignerList.length - 1
                    ];
                    signedZoneProperties.activeSignerList.pop();
                    break;
                }

                unchecked {
                    ++i;
                }
            }
        }

        // Emit an event signifying that the signer was updated.
        emit SignerUpdated(zone, signer, active);
    }

    /**
     * @notice Derive the zone address associated with a salt.
     *
     * @param salt  The salt to be used to derive the zone address.
     *
     * @return derivedAddress The derived address of the signed zone.
     */
    function getZone(bytes32 salt)
        external
        view
        override
        returns (address derivedAddress)
    {
        // Derive the SignedZone address from deployer, salt and creation code
        // hash.
        derivedAddress = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            bytes1(0xff),
                            address(this),
                            salt,
                            _SIGNED_ZONE_CREATION_CODE_HASH
                        )
                    )
                )
            )
        );
    }

    /**
     * @notice External call to return the signing information, substandards,
     *         and documentation about the zone.
     *
     * @return domainSeparator  The domain separator used for signing.
     * @return zoneName         The name of the zone.
     * @return apiEndpoint      The API endpoint for the zone.
     * @return substandards     The substandards supported by the zone.
     * @return documentationURI The documentation URI for the zone.
     */
    function getAdditionalZoneInformation(address zone)
        external
        view
        override
        returns (
            bytes32 domainSeparator,
            string memory zoneName,
            string memory apiEndpoint,
            uint256[] memory substandards,
            string memory documentationURI
        )
    {
        // Ensure the zone exists.
        _assertZoneExists(zone);

        // Return the zone's additional information.
        return _additionalZoneInformation(zone);
    }

    /**
     * @notice Internal call to return the signing information, substandards,
     *         and documentation about the zone.
     *
     * @return domainSeparator  The domain separator used for signing.
     * @return zoneName         The name of the zone.
     * @return apiEndpoint      The API endpoint for the zone.
     * @return substandards     The substandards supported by the zone.
     * @return documentationURI The documentation URI for the zone.
     */
    function _additionalZoneInformation(address zone)
        internal
        view
        returns (
            bytes32 domainSeparator,
            string memory zoneName,
            string memory apiEndpoint,
            uint256[] memory substandards,
            string memory documentationURI
        )
    {
        // Get the zone properties.
        SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

        // Return the SIP-7 information.
        domainSeparator = _domainSeparator(zone);
        zoneName = signedZoneProperties.zoneName;
        apiEndpoint = signedZoneProperties.apiEndpoint;
        substandards = signedZoneProperties.substandards;
        documentationURI = signedZoneProperties.documentationURI;
    }

    /**
     * @dev Internal view function to get the EIP-712 domain separator. If the
     *      chainId matches the chainId set on deployment, the cached domain
     *      separator will be returned; otherwise, it will be derived from
     *      scratch.
     *
     * @return The domain separator.
     */
    function _domainSeparator(address zone) internal view returns (bytes32) {
        // prettier-ignore
        return _deriveDomainSeparator(zone);
    }

    /**
     * @dev Internal view function to derive the EIP-712 domain separator.
     *
     * @return domainSeparator The derived domain separator.
     */
    function _deriveDomainSeparator(address zone)
        internal
        view
        returns (bytes32 domainSeparator)
    {
        bytes32 typehash = _EIP_712_DOMAIN_TYPEHASH;
        bytes32 nameHash = _NAME_HASH;
        bytes32 versionHash = _VERSION_HASH;

        // Leverage scratch space and other memory to perform an efficient hash.
        assembly {
            // Retrieve the free memory pointer; it will be replaced afterwards.
            let freeMemoryPointer := mload(FreeMemoryPointerSlot)

            // Retrieve value at 0x80; it will also be replaced afterwards.
            let slot0x80 := mload(Slot0x80)

            // Place typehash, name hash, and version hash at start of memory.
            mstore(0, typehash)
            mstore(OneWord, nameHash)
            mstore(TwoWords, versionHash)

            // Place chainId in the next memory location.
            mstore(ThreeWords, chainid())

            // Place the address of the signed zone contract in the next memory location.
            mstore(FourWords, zone)

            // Hash relevant region of memory to derive the domain separator.
            domainSeparator := keccak256(0, FiveWords)

            // Restore the free memory pointer.
            mstore(FreeMemoryPointerSlot, freeMemoryPointer)

            // Restore the zero slot to zero.
            mstore(ZeroSlot, 0)

            // Restore the value at 0x80.
            mstore(Slot0x80, slot0x80)
        }
    }

    /**
     * @dev Private view function to revert if the caller is not the owner of a
     *      given zone.
     *
     * @param zone The zone for which to assert ownership.
     */
    function _assertCallerIsZoneOwner(address zone) private view {
        // Ensure that the zone in question exists.
        _assertZoneExists(zone);

        // If the caller does not match the current owner of the zone...
        if (msg.sender != _signedZones[zone].owner) {
            // Revert, indicating that the caller is not the owner.
            revert CallerIsNotOwner(zone);
        }
    }

    /**
     * @dev Private view function to revert if the caller is not the owner or
     *      an active signer of a given zone.
     *
     * @param zone The zone for which to assert ownership.
     */
    function _assertCallerIsZoneOwnerOrSigner(address zone) private view {
        // Ensure that the zone in question exists.
        _assertZoneExists(zone);

        // Initialize storage variable referencing signed zone properties.
        SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

        // Ensure the caller is the owner or an active signer of the signed zone.
        if (
            msg.sender != _signedZones[zone].owner &&
            !signedZoneProperties.signers[msg.sender].active
        ) {
            // Revert, indicating that the caller is not the owner.
            revert CallerIsNotOwnerOrSigner(zone);
        }
    }

    /**
     * @dev Private view function to revert if a given zone does not exist.
     *
     * @param zone The zone for which to assert existence.
     */
    function _assertZoneExists(address zone) private view {
        // Attempt to retrieve a the owner for the zone in question.
        if (_signedZones[zone].owner == address(0)) {
            // Revert if no ownerwas located.
            revert NoZone();
        }
    }

    /**
     * @dev Private view function to revert if a signer being added to a zone
     *      is the zero address or the signer already exists, or the signer was
     *      previously authorized.  If the signer is being removed, the
     *      function will revert if the signer is not active.
     *
     * @param signedZoneProperties The signed zone properties for the zone.
     * @param signer The signer to add or remove.
     * @param active Whether the signer is being added or removed.
     */
    function _assertSignerPermissions(
        SignedZoneProperties storage signedZoneProperties,
        address signer,
        bool active
    ) private view {
        // If the signer is being added...
        if (active) {
            // Do not allow the zero address to be added as a signer.
            if (signer == address(0)) {
                revert SignerCannotBeZeroAddress();
            }

            // Revert if the signer is already added.
            if (signedZoneProperties.signers[signer].active) {
                revert SignerAlreadyAdded(signer);
            }

            // Revert if the signer was previously authorized.
            if (signedZoneProperties.signers[signer].previouslyActive) {
                revert SignerCannotBeReauthorized(signer);
            }
        } else {
            // Revert if the signer is not active.
            if (!signedZoneProperties.signers[signer].active) {
                revert SignerNotPresent(signer);
            }
        }
    }
}

File 122 of 129 : ReservoirErc1155.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract ReservoirErc1155 is ERC1155, Ownable {
  using Strings for uint256;

  // Fields

  address private royaltyRecipient;
  uint256 private royaltyBps;

  string public contractURI;

  // Constructor

  constructor(
    address _owner,
    string memory _uri,
    string memory _contractURI,
    address _royaltyRecipient,
    uint256 _royaltyBps
  ) ERC1155(_uri) {
    contractURI = _contractURI;
    royaltyRecipient = _royaltyRecipient;
    royaltyBps = _royaltyBps;

    _transferOwnership(_owner);
  }

  // Public methods

  function mint(uint256 tokenId, uint256 amount) external {
    _mint(msg.sender, tokenId, amount, "");
  }

  function uri(uint256 tokenId) public view virtual override returns (string memory) {
    return string(abi.encodePacked(super.uri(tokenId), tokenId.toString()));
  }

  // Owner methods

  function updateURI(string memory _uri) external onlyOwner {
    _setURI(_uri);
  }

  function updateContractURI(string memory _contractURI) external onlyOwner {
    contractURI = _contractURI;
  }

  function updateRoyalty(address _royaltyRecipient, uint256 _royaltyBps) external onlyOwner {
    royaltyRecipient = _royaltyRecipient;
    royaltyBps = _royaltyBps;
  }

  // EIP2981

  function royaltyInfo(
    uint256,
    uint256 price
  ) external view returns (address receiver, uint256 amount) {
    receiver = royaltyRecipient;
    amount = (price * royaltyBps) / 10000;
  }

  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    return interfaceId == 0x2a55205a || super.supportsInterface(interfaceId);
  }
}

File 123 of 129 : ReservoirErc721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract ReservoirErc721 is ERC721, Ownable {
  // Fields

  uint256 private nextTokenId;
  string private baseTokenURI;
  address private royaltyRecipient;
  uint256 private royaltyBps;

  string public contractURI;

  // Constructor

  constructor(
    address _owner,
    string memory _baseTokenURI,
    string memory _contractURI,
    address _royaltyRecipient,
    uint256 _royaltyBps
  ) ERC721("Reservoir", "RSV") {
    baseTokenURI = _baseTokenURI;
    contractURI = _contractURI;
    royaltyRecipient = _royaltyRecipient;
    royaltyBps = _royaltyBps;

    _transferOwnership(_owner);
  }

  // Public methods

  function mint() external {
    _mint(msg.sender, nextTokenId++);
  }

  // Owner methods

  function updateBaseTokenURI(string memory _baseTokenURI) external onlyOwner {
    baseTokenURI = _baseTokenURI;
  }

  function updateContractURI(string memory _contractURI) external onlyOwner {
    contractURI = _contractURI;
  }

  function updateRoyalty(address _royaltyRecipient, uint256 _royaltyBps) external onlyOwner {
    royaltyRecipient = _royaltyRecipient;
    royaltyBps = _royaltyBps;
  }

  // EIP2981

  function royaltyInfo(
    uint256,
    uint256 price
  ) external view returns (address receiver, uint256 amount) {
    receiver = royaltyRecipient;
    amount = (price * royaltyBps) / 10000;
  }

  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    return interfaceId == 0x2a55205a || super.supportsInterface(interfaceId);
  }

  // Internal methods

  function _baseURI() internal view override returns (string memory) {
    return baseTokenURI;
  }
}

File 124 of 129 : BitVectorValidator.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

import {IPropertyValidator} from "./interfaces/IPropertyValidator.sol";

// Credits: https://github.com/mzhu25/0x-property-validators
contract BitVectorValidator is IPropertyValidator {
  function validateProperty(
    address, // tokenAddress
    uint256 tokenId,
    bytes calldata propertyData
  ) external pure {
    // tokenId < propertyData.length * 8
    require(tokenId < propertyData.length << 3, "Bit vector length exceeded");

    // Bit corresponding to tokenId must be set
    require(
      uint8(propertyData[tokenId >> 3]) & (0x80 >> (tokenId & 7)) != 0,
      "Token id not in bit vector"
    );
  }
}

File 125 of 129 : IPropertyValidator.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IPropertyValidator {
  function validateProperty(
    address tokenAddress,
    uint256 tokenId,
    bytes calldata propertyData
  ) external view;
}

File 126 of 129 : PackedListValidator.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

import {IPropertyValidator} from "./interfaces/IPropertyValidator.sol";

// Credits: https://github.com/mzhu25/0x-property-validators
contract PackedListValidator is IPropertyValidator {
    function validateProperty(
        address, // tokenAddress
        uint256 tokenId,
        bytes calldata propertyData
    )
        external
        pure
    {
        (uint256 bytesPerTokenId, bytes memory packedList) = abi.decode(
            propertyData,
            (uint256, bytes)
        );

        require(
            bytesPerTokenId != 0 && bytesPerTokenId <= 32,
            "Invalid number of bytes per token id"
        );

        // Masks the lower `bytesPerTokenId` bytes of a word
        // So if `bytesPerTokenId` == 1, then bitmask = 0xff
        //    if `bytesPerTokenId` == 2, then bitmask = 0xffff, etc.
        uint256 bitMask = ~(type(uint256).max << (bytesPerTokenId << 3));
        assembly {
            // Binary search for given token id

            let left := 1
            // right = number of tokenIds in the list
            let right := div(mload(packedList), bytesPerTokenId)

            // while(left < right)
            for {} lt(left, right) {} {
                // mid = (left + right) / 2
                let mid := shr(1, add(left, right))
                // more or less equivalent to:
                // value = list[index]
                let offset := add(packedList, mul(mid, bytesPerTokenId))
                let value := and(mload(offset), bitMask)
                // if (value < tokenId) {
                //     left = mid + 1;
                //     continue;
                // }
                if lt(value, tokenId) {
                    left := add(mid, 1)
                    continue
                }
                // if (value > tokenId) {
                //     right = mid;
                //     continue;
                // }
                if gt(value, tokenId) {
                    right := mid
                    continue
                }
                // if (value == tokenId) { return; }
                stop()
            }
            // At this point left == right; check if list[left] == tokenId
            let offset := add(packedList, mul(left, bytesPerTokenId))
            let value := and(mload(offset), bitMask)
            if eq(value, tokenId) {
                stop()
            }
        }
        revert("Token id not in packed list");
    }
}

File 127 of 129 : TokenRangeValidator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IPropertyValidator} from "./interfaces/IPropertyValidator.sol";

contract TokenRangeValidator is IPropertyValidator {
  function validateProperty(
    address, // tokenAddress
    uint256 tokenId,
    bytes calldata propertyData
  ) external pure {
    (uint256 startTokenId, uint256 endTokenId) = abi.decode(propertyData, (uint256, uint256));
    require(startTokenId <= tokenId && tokenId <= endTokenId, "Token id out of range");
  }
}

File 128 of 129 : Bytes32AddressLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Library for converting between addresses and bytes32 values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol)
library Bytes32AddressLib {
    function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) {
        return address(uint160(uint256(bytesValue)));
    }

    function fillLast12Bytes(address addressValue) internal pure returns (bytes32) {
        return bytes32(bytes20(addressValue));
    }
}

File 129 of 129 : CREATE3.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {Bytes32AddressLib} from "./Bytes32AddressLib.sol";

/// @notice Deploy to deterministic addresses without an initcode factor.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/CREATE3.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol)
library CREATE3 {
    using Bytes32AddressLib for bytes32;

    //--------------------------------------------------------------------------------//
    // Opcode     | Opcode + Arguments    | Description      | Stack View             //
    //--------------------------------------------------------------------------------//
    // 0x36       |  0x36                 | CALLDATASIZE     | size                   //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 size                 //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 0 size               //
    // 0x37       |  0x37                 | CALLDATACOPY     |                        //
    // 0x36       |  0x36                 | CALLDATASIZE     | size                   //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 size                 //
    // 0x34       |  0x34                 | CALLVALUE        | value 0 size           //
    // 0xf0       |  0xf0                 | CREATE           | newContract            //
    //--------------------------------------------------------------------------------//
    // Opcode     | Opcode + Arguments    | Description      | Stack View             //
    //--------------------------------------------------------------------------------//
    // 0x67       |  0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode   | bytecode               //
    // 0x3d       |  0x3d                 | RETURNDATASIZE   | 0 bytecode             //
    // 0x52       |  0x52                 | MSTORE           |                        //
    // 0x60       |  0x6008               | PUSH1 08         | 8                      //
    // 0x60       |  0x6018               | PUSH1 18         | 24 8                   //
    // 0xf3       |  0xf3                 | RETURN           |                        //
    //--------------------------------------------------------------------------------//
    bytes internal constant PROXY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3";

    bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE);

    function deploy(
        bytes32 salt,
        bytes memory creationCode,
        uint256 value
    ) internal returns (address deployed) {
        bytes memory proxyChildBytecode = PROXY_BYTECODE;

        address proxy;
        /// @solidity memory-safe-assembly
        assembly {
            // Deploy a new contract with our pre-made bytecode via CREATE2.
            // We start 32 bytes into the code to avoid copying the byte length.
            proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt)
        }
        require(proxy != address(0), "DEPLOYMENT_FAILED");

        deployed = getDeployed(salt);
        (bool success, ) = proxy.call{value: value}(creationCode);
        require(success && deployed.code.length != 0, "INITIALIZATION_FAILED");
    }

    function getDeployed(bytes32 salt) internal view returns (address) {
        address proxy = keccak256(
            abi.encodePacked(
                // Prefix:
                bytes1(0xFF),
                // Creator:
                address(this),
                // Salt:
                salt,
                // Bytecode hash:
                PROXY_BYTECODE_HASH
            )
        ).fromLast20Bytes();

        return
            keccak256(
                abi.encodePacked(
                    // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01)
                    // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)
                    hex"d6_94",
                    proxy,
                    hex"01" // Nonce of the proxy contract (1)
                )
            ).fromLast20Bytes();
    }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"exchange","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidParams","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnsuccessfulCall","type":"error"},{"inputs":[],"name":"UnsuccessfulFill","type":"error"},{"inputs":[],"name":"UnsuccessfulPayment","type":"error"},{"inputs":[],"name":"WrongParams","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"CallExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"EXCHANGE","outputs":[{"internalType":"contract ISeaport","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ISeaport.AdvancedOrder","name":"order","type":"tuple"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"enum ISeaport.Side","name":"side","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"bytes32[]","name":"criteriaProof","type":"bytes32[]"}],"internalType":"struct ISeaport.CriteriaResolver[]","name":"criteriaResolvers","type":"tuple[]"},{"components":[{"internalType":"address","name":"fillTo","type":"address"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bool","name":"revertIfIncomplete","type":"bool"}],"internalType":"struct BaseExchangeModule.OfferParams","name":"params","type":"tuple"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.Fee[]","name":"fees","type":"tuple[]"}],"name":"acceptERC1155Offer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ISeaport.AdvancedOrder","name":"order","type":"tuple"},{"components":[{"internalType":"address","name":"fillTo","type":"address"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bool","name":"revertIfIncomplete","type":"bool"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.ERC20ListingParams","name":"params","type":"tuple"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.Fee[]","name":"fees","type":"tuple[]"}],"name":"acceptERC20Listing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ISeaport.AdvancedOrder[]","name":"orders","type":"tuple[]"},{"components":[{"internalType":"address","name":"fillTo","type":"address"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bool","name":"revertIfIncomplete","type":"bool"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.ERC20ListingParams","name":"params","type":"tuple"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.Fee[]","name":"fees","type":"tuple[]"}],"name":"acceptERC20Listings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ISeaport.AdvancedOrder[]","name":"orders","type":"tuple[]"},{"components":[{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct ISeaport.FulfillmentComponent[]","name":"offerComponents","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct ISeaport.FulfillmentComponent[]","name":"considerationComponents","type":"tuple[]"}],"internalType":"struct ISeaport.Fulfillment[]","name":"fulfillments","type":"tuple[]"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SeaportV15Module.SeaportPrivateListingWithPrice[]","name":"orders","type":"tuple[]"},{"components":[{"internalType":"address","name":"fillTo","type":"address"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bool","name":"revertIfIncomplete","type":"bool"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.ERC20ListingParams","name":"params","type":"tuple"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.Fee[]","name":"fees","type":"tuple[]"}],"name":"acceptERC20PrivateListings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ISeaport.AdvancedOrder","name":"order","type":"tuple"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"enum ISeaport.Side","name":"side","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"bytes32[]","name":"criteriaProof","type":"bytes32[]"}],"internalType":"struct ISeaport.CriteriaResolver[]","name":"criteriaResolvers","type":"tuple[]"},{"components":[{"internalType":"address","name":"fillTo","type":"address"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bool","name":"revertIfIncomplete","type":"bool"}],"internalType":"struct BaseExchangeModule.OfferParams","name":"params","type":"tuple"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.Fee[]","name":"fees","type":"tuple[]"}],"name":"acceptERC721Offer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ISeaport.AdvancedOrder","name":"order","type":"tuple"},{"components":[{"internalType":"address","name":"fillTo","type":"address"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bool","name":"revertIfIncomplete","type":"bool"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.ETHListingParams","name":"params","type":"tuple"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.Fee[]","name":"fees","type":"tuple[]"}],"name":"acceptETHListing","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ISeaport.AdvancedOrder","name":"order","type":"tuple"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SeaportV15Module.SeaportETHListingWithPrice[]","name":"orders","type":"tuple[]"},{"components":[{"internalType":"address","name":"fillTo","type":"address"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bool","name":"revertIfIncomplete","type":"bool"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.ETHListingParams","name":"params","type":"tuple"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.Fee[]","name":"fees","type":"tuple[]"}],"name":"acceptETHListings","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct ISeaport.AdvancedOrder[]","name":"orders","type":"tuple[]"},{"components":[{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct ISeaport.FulfillmentComponent[]","name":"offerComponents","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct ISeaport.FulfillmentComponent[]","name":"considerationComponents","type":"tuple[]"}],"internalType":"struct ISeaport.Fulfillment[]","name":"fulfillments","type":"tuple[]"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SeaportV15Module.SeaportPrivateListingWithPrice[]","name":"orders","type":"tuple[]"},{"components":[{"internalType":"address","name":"fillTo","type":"address"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bool","name":"revertIfIncomplete","type":"bool"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.ETHListingParams","name":"params","type":"tuple"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct BaseExchangeModule.Fee[]","name":"fees","type":"tuple[]"}],"name":"acceptETHPrivateListings","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address[]","name":"targets","type":"address[]"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"makeCalls","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct ISeaport.OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ISeaport.ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct ISeaport.ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum ISeaport.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct ISeaport.OrderParameters","name":"parameters","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct ISeaport.Order[]","name":"orders","type":"tuple[]"},{"components":[{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct ISeaport.FulfillmentComponent[]","name":"offerComponents","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct ISeaport.FulfillmentComponent[]","name":"considerationComponents","type":"tuple[]"}],"internalType":"struct ISeaport.Fulfillment[]","name":"fulfillments","type":"tuple[]"}],"name":"matchOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c0346200014b57601f62003f3e38819003918201601f19168301916001600160401b0383118484101762000150578084926060946040528339810103126200014b576200004d8162000166565b620000696040620000616020850162000166565b930162000166565b600080546001600160a01b0319166001600160a01b03938416908117825560405194917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a360016002556080521660a052613dc290816200017c82396080518181816103fb0152611704015260a05181818161088e01528181610cfb01528181611348015281816115ce01528181611b5f015281816125d9015281816131db0152818161324d0152818161351e0152818161363c0152818161381201528181613882015281816138da0152818161396801528181613bf30152613cd20152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200014b5756fe6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c806312f3a43f14610197578063150b7a021461018e5780631626ba7e146101855780632b8a88ec1461017c5780634e71e0c814610173578063590823091461016a5780636baab5f71461016157806376af66291461015857806380b102ff1461014f57806386f20e8c146101465780638da5cb5b1461013d578063a0810f3614610134578063a81744041461012b578063a87d645c14610122578063b50e44b814610119578063e30c397814610110578063f23a6e6114610107578063f2fde38b146100fe5763f887ea400361000e576100f96116ed565b61000e565b506100f961169c565b506100f9611627565b506100f96115fd565b506100f96115b7565b506100f9611427565b506100f9611298565b506100f9611256565b506100f961122c565b506100f96111a8565b506100f961104e565b506100f9610e8f565b506100f9610c75565b506100f9610baf565b506100f9610af0565b506100f96107d6565b506100f961056b565b506100f961038b565b506100f96101d5565b9181601f840112156101d0578235916001600160401b0383116101d0576020808501948460051b0101116101d057565b600080fd5b506060806003193601126101d0576001600160401b03906004358281116101d0576102049036906004016101a0565b6024939193358281116101d05761021f9036906004016101a0565b926044359081116101d0576102389036906004016101a0565b60005491956001600160a01b039490928516330361032f576102586117df565b60005b81811061026c576100196001600255565b807fa3f06cf374cf66be06f5fe85cdd3b13d9d9fdef6482f640d2de1d44c3ed7332c8787868c6103228f878f81816102f3828f60019f976102ee8c8e6102e88e6102df886103069f806102fe9f6102c76102d7938d8d61174a565b35976102d289610340565b61176f565b96909361174a565b35933691610534565b9061189f565b61174a565b35986102d28a610340565b95909461174a565b359160409384519687961686528c60208701528c8601916117be565b918301520390a10161025b565b6040516282b42960e81b8152600490fd5b6001600160a01b038116036101d057565b359061035c82610340565b565b9181601f840112156101d0578235916001600160401b0383116101d057602083818601950101116101d057565b50346101d05760803660031901126101d0576103a8600435610340565b6103b3602435610340565b6064356001600160401b0381116101d0576103d290369060040161035e565b806103ea575b604051630a85bd0160e11b8152602090f35b61041f916103f9913691610534565b7f0000000000000000000000000000000000000000000000000000000000000000611863565b38806103d8565b50634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b0382111761045857604052565b610460610426565b604052565b604081019081106001600160401b0382111761045857604052565b6001600160401b03811161045857604052565b606081019081106001600160401b0382111761045857604052565b60c081019081106001600160401b0382111761045857604052565b90601f801991011681019081106001600160401b0382111761045857604052565b6040519061016082018281106001600160401b0382111761045857604052565b6020906001600160401b038111610527575b601f01601f19160190565b61052f610426565b61051c565b9291926105408261050a565b9161054e60405193846104c9565b8294818452818301116101d0578281602093846000960137010152565b50346101d05760403660031901126101d0576024356001600160401b0381116101d057366023820112156101d0576105ad903690602481600401359101610534565b50604051630b135d3f60e11b8152602090f35b60a09060231901126101d057602490565b908160a09103126101d05790565b6020906001600160401b0381116105f8575b60051b0190565b610600610426565b6105f1565b81601f820112156101d05780359161061c836105df565b9261062a60405194856104c9565b808452602092838086019260051b8201019283116101d0578301905b828210610654575050505090565b81358152908301908301610646565b60609060431901126101d057604490565b9181601f840112156101d0578235916001600160401b0383116101d0576020808501948460061b0101116101d057565b60c06003198201126101d0576001600160401b03906004358281116101d057816106d0916004016105d1565b9260248035908482116101d057836023830112156101d0578160040135916106f7836105df565b926040610706815195866104c9565b818552602093808587019360051b850101938885116101d057818101935b85851061075757505050505050509261073c83610663565b9260a4359182116101d05761075391600401610674565b9091565b84358b81116101d05782019060a0828c0360231901126101d05784519061077d8261043d565b848301358252604483013560028110156101d057898301526064830135868301526084830135606083015260a4830135918d83116101d0576107c68d878c969587960101610605565b6080820152815201940193610724565b50346101d0576107e5366106a4565b6107f1949192946117df565b61081161080b61080184806126af565b60608101906126c5565b906126fa565b92600361081d8561273e565b6108268161272c565b141580610ac9575b610ab75760209161084f610843848701611762565b6001600160a01b031690565b9361097461095a61087d6108438761087761080b61086d88806126af565b6040810190612748565b01611762565b986001600160a01b039887906108bf7f00000000000000000000000000000000000000000000000000000000000000008c166108b9818d61296a565b8d611dee565b60036108ca8261273e565b6108d38161272c565b03610aa15760400135809a5b604051627eeac760e11b808252306004830152602482018490529094918c16918f8587604481875afa968715610a94575b600097610a6e575b5060406109259101611948565b15610a5d576109369030908a6134be565b60405190815230600482015260248101929092529093849190829081906044820190565b03915afa918215610a50575b600092610a21575b506118d8565b91826109ac575b6109a28861099d8b6109988b8b6109938c8501611762565b612a37565b611762565b612818565b6100196001600255565b60005b8181106109bc575061097b565b80610a1b8a6109d66109d1600195878b6118ed565b6127b7565b8051610a15906109f5908a908d906001600160a01b031694015161190b565b610a0f610a038d8b01612a2d565b6001600160781b031690565b9061191e565b90612805565b016109af565b610a42919250873d8911610a49575b610a3a81836104c9565b810190611a09565b903861096e565b503d610a30565b610a58611a18565b610966565b610a699030908a6131b3565b610936565b610925919750610a8c604091883d8a11610a4957610a3a81836104c9565b979150610918565b610a9c611a18565b610910565b506060610aad8361277d565b510151809a6108df565b604051635863f78960e01b8152600490fd5b506005610ad58561273e565b610ade8161272c565b141561082e565b60009103126101d057565b50346101d057600080600319360112610b57576001546001600160a01b038116903382900361032f5782546001600160a01b03199081168317845516600155807f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b80fd5b60e06003198201126101d0576001600160401b03906004358281116101d05781610b86916004016101a0565b9093909260a06023198401126101d05760249260c4359182116101d05761075391600401610674565b50346101d057610bf1610bc136610b5a565b91610bce959394956117df565b602081013594610bdd86610340565b606082013596610bec88610340565b6120b4565b6040516370a0823160e01b8152306004820152906020826024816001600160a01b0387165afa918215610c68575b600092610c48575b5081610c37576100196001600255565b610c4092611bda565b3880806109a2565b610c6191925060203d8111610a4957610a3a81836104c9565b9038610c27565b610c70611a18565b610c1f565b50346101d057610c84366106a4565b610c929492949391936117df565b610ca261080b61080185806126af565b936002610cae8661273e565b610cb78161272c565b141580610e62575b610ab757602091610cd4610843848801611762565b93610cec6108438561087761080b61086d8b806126af565b966001600160a01b0396610d2c7f00000000000000000000000000000000000000000000000000000000000000008916610d26818a61296a565b8a611dee565b610d3860408b01611948565b15610e5157610d49908330916134be565b6002610d548261273e565b610d5d8161272c565b03610e3c57604091500135945b6040516331a9108f60e11b8152600481018790529084826024818985165afa918215610e2f575b600092610e00575b5030911603610dc0575b6109a28661099d896109988989610dbb8a8501611762565b612886565b60005b818110610dd05750610da3565b80610dfa88610de56109d160019587896118ed565b805190880151906001600160a01b0316612805565b01610dc3565b610e21919250853d8711610e28575b610e1981836104c9565b8101906127a2565b9038610d99565b503d610e0f565b610e37611a18565b610d91565b50610e4860609161277d565b51015194610d6a565b610e5d908330916131b3565b610d49565b506004610e6e8661273e565b610e778161272c565b1415610cbf565b60809060231901126101d057602490565b5060c03660031901126101d0576001600160401b036004358181116101d057610ebc9036906004016105d1565b610ec536610e7e565b9160a4359081116101d057610ede903690600401610674565b929091610ee96117df565b60208083013593610ef985610340565b60608401359580610f3257505050610f12929350611983565b4780610f22576100196001600255565b610f2b916119ca565b38806109a2565b919392610f7491938747926040830135610f4b8161193e565b15610fd757610f6d92610f5c611952565b903591610f6883610340565b6135e4565b47906118d8565b60005b828110610f8a5750505050509050610f12565b80610fad88610fa88589610fa16001978a8c6118ed565b013561190b565b61191e565b80610fba575b5001610f77565b610fd190610fcc61099884888a6118ed565b6119ca565b38610fb3565b610ff492610fe3611952565b903591610fef83610340565b613223565b610f6d565b60c06003198201126101d0576001600160401b03906004358281116101d05781611025916004016101a0565b9093909260806023198401126101d05760249260a4359182116101d05761075391600401610674565b5061105836610ff9565b936110649391936117df565b6020808401359461107486610340565b6060850135968061108d57505050610f12939450612011565b929194939093479261109d611952565b6110a960408401611948565b156111585760005b84811061111a5750505050506110c89047906118d8565b60005b8281106110de5750505050509050610f12565b806110f588610fa88589610fa16001978a8c6118ed565b80611102575b50016110cb565b61111490610fcc61099884888a6118ed565b386110fb565b8061115261113461112e6001948988611fcc565b80611ffc565b61113d87611762565b858d61114a868c8b611fcc565b0135926135e4565b016110b1565b60005b8481106111705750505050506110c890610f6d565b806111a261118461112e6001948988611fcc565b61118d87611762565b858d61119a868c8b611fcc565b013592613223565b0161115b565b50346101d05760e03660031901126101d0576001600160401b036004358181116101d0576111da9036906004016105d1565b906111e4366105c0565b9060c4359081116101d057611200610bf1913690600401610674565b906112096117df565b60208401359361121885610340565b60608101359561122787610340565b611a25565b50346101d05760003660031901126101d0576000546040516001600160a01b039091168152602090f35b50346101d057610bf161126836610b5a565b91611275959394956117df565b60208101359461128486610340565b60608201359661129388610340565b6124bd565b50346101d057604060031981813601126101d0576001600160401b03906004358281116101d0576112cd9036906004016101a0565b90926024359081116101d057916112e9859336906004016101a0565b916112f26117df565b8451958694632a05d10160e21b8652806044870188600489015252606486019160648260051b8801019781936000925b8484106113b75789600081806113438f8e8e8e858403016024860152612f89565b0381837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af180156113aa575b611387576100196001600255565b6113a3903d806000833e61139b81836104c9565b810190612b36565b50806109a2565b6113b2611a18565b611379565b919395969798509193986114066001916063198d82030185526114136113dd8d86612f02565b916113f76113eb8480612c3c565b89835289830190612dfd565b90602094848680960190612ed1565b91858185039101526117be565b9b019301940191938a989796959391611322565b5061143136610ff9565b9361143d9391936117df565b6020808401359461144d86610340565b6060850135968061146657505050610f12939450612410565b9294909391944792611476611952565b90604093611485858201611948565b156115625760005b898382106114f957505050505050506114a79047906118d8565b60005b8281106114bd5750505050509050610f12565b806114d488610fa88589610fa16001978a8c6118ed565b806114e1575b50016114aa565b6114f390610fcc61099884888a6118ed565b386114da565b9061155c61154687808888611556898e61154c8a8561153e60019e6115358461152d611527828f8890612279565b806122a9565b9b909d612279565b908101906122a9565b9b9095611762565b99612279565b0135963691612360565b9261393d565b0161148d565b60005b8983821061157d57505050505050506114a790610f6d565b906115b1611546878088886115ab898e61154c8a8561153e60019e6115358461152d611527828f8890612279565b92613857565b01611565565b50346101d05760003660031901126101d0576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101d05760003660031901126101d0576001546040516001600160a01b039091168152602090f35b50346101d05760a03660031901126101d057611644600435610340565b61164f602435610340565b6084356001600160401b0381116101d05761166e90369060040161035e565b80611686575b60405163f23a6e6160e01b8152602090f35b611695916103f9913691610534565b3880611674565b50346101d05760203660031901126101d0576004356116ba81610340565b6000546001600160a01b0391908216330361032f57166bffffffffffffffffffffffff60a01b6001541617600155600080f35b50346101d05760003660031901126101d0576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50634e487b7160e01b600052603260045260246000fd5b919081101561175a5760051b0190565b610600611733565b3561176c81610340565b90565b91908110156117b1575b60051b81013590601e19813603018212156101d05701908135916001600160401b0383116101d05760200182360381136101d0579190565b6117b9611733565b611779565b908060209392818452848401376000828201840152601f01601f1916010190565b60028054146117ee5760028055565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b3d1561185e573d906118448261050a565b9161185260405193846104c9565b82523d6000602084013e565b606090565b8151600092839260209091019083906001600160a01b03165af1611885611833565b501561188d57565b6040516322092f2f60e11b8152600490fd5b8151600093849391926020909201916001600160a01b03165af1611885611833565b50634e487b7160e01b600052601160045260246000fd5b919082039182116118e557565b61035c6118c1565b91908110156118fe575b60061b0190565b611906611733565b6118f7565b818102929181159184041417156118e557565b8115611928570490565b634e487b7160e01b600052601260045260246000fd5b801515036101d057565b3561176c8161193e565b604051602081018181106001600160401b03821117611976575b6040526000815290565b61197e610426565b61196c565b60408201356119918161193e565b156119b05761035c916119a2611952565b606082359261114a84610340565b61035c916119bc611952565b606082359261119a84610340565b816119d3575050565b6000918291829182916001600160a01b03165af16119ef611833565b50156119f757565b60405163d2dcf4f360e01b8152600490fd5b908160209103126101d0575190565b506040513d6000823e3d90fd5b9091939293611a3660608401611762565b60808401359580611a4f5750505061035c929350611b46565b6040516370a0823160e01b808252306004830152602096949593949293611abe936001600160a01b038716939289929091908385602481895afa948515611b39575b600095611b14575b5090611aa491611b46565b60405190815230600482015292839081806024810161095a565b60005b828110611ad2575050505050509050565b80611ae989610fa8858a610fa16001978a8d6118ed565b80611af6575b5001611ac1565b611b0e90611b0861099884888b6118ed565b87611bda565b38611aef565b611aa492919550611b3190853d8711610a4957610a3a81836104c9565b949091611a99565b611b41611a18565b611a91565b611b8d6060830135611b5781610340565b6080840135907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690611ee0565b6040820135611b9b8161193e565b15611bbd5761035c91611bac611952565b903591611bb883610340565b6134be565b61035c91611bc9611952565b903591611bd583610340565b6131b3565b60405163a9059cbb60e01b60208083019182526001600160a01b039490941660248301526044808301959095529381529192611c7392916000908190611c216064866104c9565b60018060a01b03169260405194611c3786610465565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488870152519082855af1611c6d611833565b91611d5c565b805190828215928315611ce3575b50505015611c8c5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b611cf39350820181019101611cfb565b388281611c81565b908160209103126101d0575161176c8161193e565b15611d1757565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b91929015611d7c5750815115611d70575090565b61176c903b1515611d10565b825190915015611d8f5750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b828510611dd5575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350611db2565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526020939216908381604481855afa908115611ed3575b600091611eb6575b5060001911611e3d57505050565b60405163095ea7b360e01b81526001600160a01b0392909216600483015260001960248301528290829060449082906000905af18015611ea9575b611e80575050565b81611e9f92903d10611ea2575b611e9781836104c9565b810190611cfb565b50565b503d611e8d565b611eb1611a18565b611e78565b611ecd9150843d8611610a4957610a3a81836104c9565b38611e2f565b611edb611a18565b611e27565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152602094921692919081908581604481885afa908115611fbf575b600091611fa2575b5010611f32575b50505050565b60405163095ea7b360e01b81526001600160a01b039290921660048301526024820152908290829060449082906000905af18015611f95575b611f77575b8080611f2c565b81611f8d92903d10611ea257611e9781836104c9565b503880611f70565b611f9d611a18565b611f6b565b611fb99150863d8811610a4957610a3a81836104c9565b38611f25565b611fc7611a18565b611f1d565b9190811015611fef575b60051b81013590603e19813603018212156101d0570190565b611ff7611733565b611fd6565b903590609e19813603018212156101d0570190565b90929161201c611952565b9161202960408301611948565b156120715760005b85811061204057505050509050565b8061206b61205461112e6001948a87611fcc565b61205d86611762565b87602061114a868d8a611fcc565b01612031565b60005b85811061208357505050509050565b806120ae61209761112e6001948a87611fcc565b6120a086611762565b87602061119a868d8a611fcc565b01612074565b9091929493946120c660608501611762565b608085013596806120df5750505061035c9394506121de565b6040516370a0823160e01b808252306004830152602097949693959294612134946001600160a01b038816948a9392919084866024818a5afa9586156121ab575b600096612184575b5090611aa492916121de565b60005b828110612148575050505050509050565b8061215f89610fa8858a610fa16001978a8d6118ed565b8061216c575b5001612137565b61217e90611b0861099884888b6118ed565b38612165565b611aa493929196506121a290863d8811610a4957610a3a81836104c9565b95909192612128565b6121b3611a18565b612120565b909161176c928110156121d1575b60051b810190611ffc565b6121d9611733565b6121c6565b91906121ef611b5760608401611762565b6121f7611952565b9061220460408401611948565b156122415760005b81811061221a575050505050565b8061223b61222b60019385896121b8565b8561223588611762565b916134be565b0161220c565b60005b818110612252575050505050565b8061227361226360019385896121b8565b8561226d88611762565b916131b3565b01612244565b919081101561229c575b60051b81013590605e19813603018212156101d0570190565b6122a4611733565b612283565b903590601e19813603018212156101d057018035906001600160401b0382116101d057602001918160051b360383136101d057565b81601f820112156101d0578035906122f5826105df565b92604092612305845195866104c9565b808552602091828087019260061b850101938185116101d0578301915b8483106123325750505050505090565b85838303126101d057838691825161234981610465565b853581528286013583820152815201920191612322565b9092919261236d816105df565b9160409161237d835194856104c9565b839581855260208095019160051b8301938185116101d05783925b8584106123a85750505050505050565b6001600160401b039084358281116101d05786019083828603126101d05783516123d181610465565b82358481116101d057866123e69185016122de565b8152898301359384116101d057612401868b958695016122de565b83820152815201930192612398565b90612419611952565b604093612427858201611948565b156124825760005b83811061243e57505050505050565b8061247c61154687878785611556898e61154c61246161152760019d898b612279565b95909761153e61247283838d612279565b60208101906122a9565b0161242f565b60005b83811061249457505050505050565b806124b7611546878787856115ab898e61154c61246161152760019d898b612279565b01612485565b9091929493946124cf60608501611762565b608085013596806124e85750505061035c9394506125c1565b6040516370a0823160e01b80825230600483015260209794969395929461253d946001600160a01b038816948a9392919084866024818a5afa9586156125b4575b60009661258d575b5090611aa492916125c1565b60005b828110612551575050505050509050565b8061256889610fa8858a610fa16001978a8d6118ed565b80612575575b5001612540565b61258790611b0861099884888b6118ed565b3861256e565b611aa493929196506125ab90863d8811610a4957610a3a81836104c9565b95909192612531565b6125bc611a18565b612529565b906126076125d160608501611762565b6080850135907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690611ee0565b61260f611952565b9061261c60408501611948565b1561267a5760005b818110612632575050505050565b8061267461266e8561264a611527600196888b612279565b61265b612472878a8d969596612279565b94906126668d611762565b953691612360565b926138b3565b01612624565b60005b81811061268b575050505050565b806126a96126a38561264a611527600196888b612279565b926137eb565b0161267d565b90359061015e19813603018212156101d0570190565b903590601e19813603018212156101d057018035906001600160401b0382116101d0576020019160c08202360383136101d057565b90156127035790565b61176c611733565b600611156101d057565b50634e487b7160e01b600052602160045260246000fd5b6006111561273657565b61035c612715565b3561176c8161270b565b903590601e19813603018212156101d057018035906001600160401b0382116101d0576020019160a08202360383136101d057565b60209080511561278b570190565b612793611733565b0190565b519061035c82610340565b908160209103126101d0575161176c81610340565b6040813603126101d057602060405191604083018381106001600160401b038211176127f8575b60405280356127ec81610340565b83520135602082015290565b612800610426565b6127de565b8161280f57505050565b61035c92611bda565b6040516370a0823160e01b8152306004820152906020826024816001600160a01b0387165afa918215612879575b600092612859575b508161280f57505050565b61287291925060203d8111610a4957610a3a81836104c9565b903861284e565b612881611a18565b612846565b6040516331a9108f60e11b8152600481018490526001600160a01b03928316939192602082602481885afa91821561295d575b60009261293d575b501630146128ce57505050565b823b156101d057604051632142170760e11b81523060048201526001600160a01b0390921660248301526044820152906000908290818381606481015b03925af18015612930575b61291d5750565b8061292a61035c92610480565b80610ae5565b612938611a18565b612916565b61295691925060203d8111610e2857610e1981836104c9565b90386128c1565b612965611a18565b6128b9565b60405163e985e9c560e01b81523060048201526001600160a01b0383811660248301529190911690602081604481855afa908115612a0f575b6000916129f1575b50156129b5575050565b803b156101d05760405163a22cb46560e01b81526001600160a01b0390921660048301526001602483015260009082908183816044810161290b565b612a09915060203d8111611ea257611e9781836104c9565b386129ab565b612a17611a18565b6129a3565b6001600160781b038116036101d057565b3561176c81612a1c565b604051627eeac760e11b81523060048201526024810184905290916001600160a01b0316602082604481845afa918215612b29575b600092612b09575b5081612a805750505050565b803b156101d057604051637921219560e11b81523060048201526001600160a01b039390931660248401526044830193909352606482015260a06084820152600060a482018190529091829060c490829084905af18015612afc575b612ae9575b808080611f2c565b8061292a612af692610480565b38612ae1565b612b04611a18565b612adc565b612b2291925060203d8111610a4957610a3a81836104c9565b9038612a74565b612b31611a18565b612a6c565b60209081818403126101d0578051906001600160401b0382116101d0570182601f820112156101d057805191612b6b836105df565b936040612b7a815196876104c9565b848652828601918360e0809702860101948186116101d0578401925b858410612ba7575050505050505090565b8382038781126101d057835191612bbd83610493565b60a08092126101d0578892612c2488938751612bd88161043d565b8951612be38161270b565b8152858a0151612bf281610340565b86820152888a0151898201526060808b0151908201526080808b015190612c1882610340565b82015283528801612797565b8382015260c087015186820152815201930192612b96565b903561015e19823603018112156101d0570190565b9035601e19823603018112156101d05701602081359101916001600160401b0382116101d05760a08202360383136101d057565b906006821015612c925752565b612c9a612715565b52565b9190808252602080920192916000905b828210612cbb575050505090565b9091929380612cd66001928735612cd18161270b565b612c85565b82860135612ce381610340565b828060a01b03168382015260408087013590820152606080870135908201526080808701359082015260a08091019501920190929192612cad565b9035601e19823603018112156101d05701602081359101916001600160401b0382116101d05760c08202360383136101d057565b9190808252602080920192916000905b828210612d70575050505090565b9091929380612d866001928735612cd18161270b565b82860135612d9381610340565b828060a01b038091168483015260408088013590830152606080880135908301526080808801359083015260a09081880135612dce81610340565b169082015260c0908101950193920190612d62565b359060048210156101d057565b906004821015612c925752565b90612e1881612e0b84610351565b6001600160a01b03169052565b612e37612e2760208401610351565b6001600160a01b03166020830152565b612e76612e5b612e4a6040850185612c51565b610160806040870152850191612c9d565b612e686060850185612d1e565b908483036060860152612d52565b91612e90612e8660808301612de3565b6080840190612df0565b60a081013560a083015260c081013560c083015260e081013560e0830152610100808201359083015261012080820135908301526101408091013591015290565b9035601e19823603018112156101d05701602081359101916001600160401b0382116101d05781360383136101d057565b9035603e19823603018112156101d0570190565b9035601e19823603018112156101d05701602081359101916001600160401b0382116101d0578160061b360383136101d057565b9190808252602080920192916000905b828210612f68575050505090565b83358552838101358582015260409485019490930192600190910190612f5a565b90808352602080930192838260051b850194846000925b858410612fb1575050505050505090565b9091929394959685806130048385600195038852612fcf8c88612f02565b90612ff7612fed612fe08480612f16565b6040808652850191612f4a565b9285810190612f16565b9185818503910152612f4a565b990194019401929594939190612fa0565b61176c9161309261308761303a61302c8580612c3c565b60a0855260a0850190612dfd565b602085013561304881612a1c565b6001600160781b038091166020860152604086013561306681612a1c565b1660408501526130796060860186612ed1565b9085830360608701526117be565b926080810190612ed1565b9160808185039101526117be565b90815180825260208092019182818360051b8201950193600080925b8584106130cd575050505050505090565b9091929394959681810384528751908660c060a0928381019385518252838601516002811015613165575b8483015260408087015190830152606080870151908301526080958601519582015284519384905291939101919083019085905b80821061314c5750505090806001929901940194019295949391906130bc565b919380600192948651815201940192018893929161312c565b61316d612715565b6130f8565b9161319c9061318e606093969596608086526080860190613015565b9084820360208601526130a0565b600060408401526001600160a01b03909416910152565b906020916131d560405194859384936339eb2ac960e21b855260048501613172565b038160007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af161320c5750565b611e9f9060203d8111611ea257611e9781836104c9565b926020929161324994604051958694859384936339eb2ac960e21b855260048501613172565b03917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af161320c5750565b81601f820112156101d057803590613295826105df565b926040906132a5825195866104c9565b838552602091828601918360a0809702860101948186116101d0578401925b8584106132d5575050505050505090565b86848303126101d05784879184516132ec8161043d565b86356132f78161270b565b81528287013561330681610340565b83820152858701358682015260608088013590820152608080880135908201528152019301926132c4565b81601f820112156101d057803590613348826105df565b92604090613358825195866104c9565b838552602091828601918360c0809702860101948186116101d0578401925b858410613388575050505050505090565b86848303126101d057848791845161339f816104ae565b86356133aa8161270b565b8152828701356133b981610340565b838201528587013586820152606080880135908201526080808801359082015260a080880135906133e982610340565b820152815201930192613377565b610160813603126101d05761340a6104ea565b9061341481610351565b825261342260208201610351565b60208301526001600160401b0360408201358181116101d057613448903690840161327e565b604084015260608201359081116101d0576134669036908301613331565b606083015261347760808201612de3565b608083015260a081013560a083015260c081013560c083015260e081013560e083015261010080820135908301526101208082013590830152610140809101359082015290565b916134d96134d46134cf85806126af565b6133f7565b613bc0565b91613519604085019160206134f96134f3610a0386612a2d565b87613cb5565b9460009260405194859283926339eb2ac960e21b84528b60048501613172565b0381847f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af19091816135c4575b5061356757604051631298f31b60e11b8152600490fd5b61357d57604051631298f31b60e11b8152600490fd5b6135a4610a039261359f6135ab95613599610a03602096612a2d565b90613cb5565b6118d8565b9301612a2d565b036135b257565b604051631298f31b60e11b8152600490fd5b6135dd91925060203d8111611ea257611e9781836104c9565b9038613550565b9290916136386135fa6134d46134cf87806126af565b9360206040870193613617613611610a0387612a2d565b88613cb5565b956000936040518096819482936339eb2ac960e21b84528d60048501613172565b03917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af19091816135c4575061356757604051631298f31b60e11b8152600490fd5b90815180825260208080930193019160005b8281106136a4575050505090565b8351805186528201518583015260409094019392810192600101613696565b90815180825260208092019182818360051b82019501936000915b8483106136ee5750505050505090565b909192939495848061372783856001950387528a51908361371783516040808552840190613684565b9201519084818403910152613684565b98019301930191949392906136de565b949290959391958660808701608088525260a086019660a08160051b8801019782600090815b8483106137a85750505050505060609161378787613795938861035c98999a0360208b01526130a0565b9087820360408901526136c3565b6001600160a01b03909216940193909352565b90919293949a609f198b82030185528b35609e19843603018112156137e757600191846137d59201613015565b9b60209081019695019301919061375d565b8280fd5b604051637968958960e11b8152946000948694859461380d9460048701613737565b0381837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af16138435750565b611e9f903d806000833e61139b81836104c9565b61387e6000959693949660405197889687958695637968958960e11b875260048701613737565b03917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af16138435750565b604051637968958960e11b815294600094869485946138d59460048701613737565b0381837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af19081613922575b5061035c57604051631298f31b60e11b8152600490fd5b613936903d806000833e61139b81836104c9565b503861390b565b6139646000959693949660405197889687958695637968958960e11b875260048701613737565b03917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af19081613922575061035c57604051631298f31b60e11b8152600490fd5b6040519061016082018281106001600160401b03821117613a15575b60405281610140600091828152826020820152606060408201526060808201528260808201528260a08201528260c08201528260e082015282610100820152826101208201520152565b613a1d610426565b6139cb565b90815180825260208080930193019160005b828110613a42575050505090565b909192938260a06001928751613a59828251612c85565b8084015185841b869003168285015260408082015190830152606080820151908301526080908101519082015201950193929101613a34565b90815180825260208080930193019160005b828110613ab2575050505090565b909192938260c06001928751613ac9828251612c85565b848060a01b038085830151168584015260408083015190840152606080830151908401526080808301519084015260a0809201511690820152019501910192919092613aa4565b602080825282516001600160a01b03169082015260208201516001600160a01b031660408201526040820151613b6d613b5761016092836060860152610180850190613a22565b6060850151848203601f19016080860152613a92565b92613b80608082015160a0850190612df0565b60a081015160c084015260c081015160e084015260e081015161010090818501528101516101209081850152810151906101409182850152015191015290565b613bc86139af565b50805160405163f07ec37360e01b81526001600160a01b039182166004820152602092613c509284927f0000000000000000000000000000000000000000000000000000000000000000909116908381602481855afa908115613ca8575b600091613c8b575b5061014083015260405180809581946379df72bd60e01b835260048301613b10565b03915afa918215613c7e575b600092613c6857505090565b61176c9250803d10610a4957610a3a81836104c9565b613c86611a18565b613c5c565b613ca29150843d8611610a4957610a3a81836104c9565b38613c2e565b613cb0611a18565b613c26565b6040516346423aa760e01b815260048101919091526080816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115613d7f575b6000908192613d2c575b5060008215613d25575061176c92610fa89161190b565b9250505090565b91506080823d8211613d77575b81613d46608093836104c9565b81010312610b575750613d59815161193e565b613d66602082015161193e565b606060408201519101519038613d0e565b3d9150613d39565b613d87611a18565b613d0456fea2646970667358221220476b64bb30b77ad423a331edf7298001176966c712f6569bbdb76b003767694764736f6c63430008110033000000000000000000000000f3d63166f0ca56c3c1a3508fce03ff0cf3fb691e0000000000000000000000001aed60a97192157fda7fb26267a439d523d09c5e00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc

Deployed Bytecode

0x6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c806312f3a43f14610197578063150b7a021461018e5780631626ba7e146101855780632b8a88ec1461017c5780634e71e0c814610173578063590823091461016a5780636baab5f71461016157806376af66291461015857806380b102ff1461014f57806386f20e8c146101465780638da5cb5b1461013d578063a0810f3614610134578063a81744041461012b578063a87d645c14610122578063b50e44b814610119578063e30c397814610110578063f23a6e6114610107578063f2fde38b146100fe5763f887ea400361000e576100f96116ed565b61000e565b506100f961169c565b506100f9611627565b506100f96115fd565b506100f96115b7565b506100f9611427565b506100f9611298565b506100f9611256565b506100f961122c565b506100f96111a8565b506100f961104e565b506100f9610e8f565b506100f9610c75565b506100f9610baf565b506100f9610af0565b506100f96107d6565b506100f961056b565b506100f961038b565b506100f96101d5565b9181601f840112156101d0578235916001600160401b0383116101d0576020808501948460051b0101116101d057565b600080fd5b506060806003193601126101d0576001600160401b03906004358281116101d0576102049036906004016101a0565b6024939193358281116101d05761021f9036906004016101a0565b926044359081116101d0576102389036906004016101a0565b60005491956001600160a01b039490928516330361032f576102586117df565b60005b81811061026c576100196001600255565b807fa3f06cf374cf66be06f5fe85cdd3b13d9d9fdef6482f640d2de1d44c3ed7332c8787868c6103228f878f81816102f3828f60019f976102ee8c8e6102e88e6102df886103069f806102fe9f6102c76102d7938d8d61174a565b35976102d289610340565b61176f565b96909361174a565b35933691610534565b9061189f565b61174a565b35986102d28a610340565b95909461174a565b359160409384519687961686528c60208701528c8601916117be565b918301520390a10161025b565b6040516282b42960e81b8152600490fd5b6001600160a01b038116036101d057565b359061035c82610340565b565b9181601f840112156101d0578235916001600160401b0383116101d057602083818601950101116101d057565b50346101d05760803660031901126101d0576103a8600435610340565b6103b3602435610340565b6064356001600160401b0381116101d0576103d290369060040161035e565b806103ea575b604051630a85bd0160e11b8152602090f35b61041f916103f9913691610534565b7f0000000000000000000000001aed60a97192157fda7fb26267a439d523d09c5e611863565b38806103d8565b50634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b0382111761045857604052565b610460610426565b604052565b604081019081106001600160401b0382111761045857604052565b6001600160401b03811161045857604052565b606081019081106001600160401b0382111761045857604052565b60c081019081106001600160401b0382111761045857604052565b90601f801991011681019081106001600160401b0382111761045857604052565b6040519061016082018281106001600160401b0382111761045857604052565b6020906001600160401b038111610527575b601f01601f19160190565b61052f610426565b61051c565b9291926105408261050a565b9161054e60405193846104c9565b8294818452818301116101d0578281602093846000960137010152565b50346101d05760403660031901126101d0576024356001600160401b0381116101d057366023820112156101d0576105ad903690602481600401359101610534565b50604051630b135d3f60e11b8152602090f35b60a09060231901126101d057602490565b908160a09103126101d05790565b6020906001600160401b0381116105f8575b60051b0190565b610600610426565b6105f1565b81601f820112156101d05780359161061c836105df565b9261062a60405194856104c9565b808452602092838086019260051b8201019283116101d0578301905b828210610654575050505090565b81358152908301908301610646565b60609060431901126101d057604490565b9181601f840112156101d0578235916001600160401b0383116101d0576020808501948460061b0101116101d057565b60c06003198201126101d0576001600160401b03906004358281116101d057816106d0916004016105d1565b9260248035908482116101d057836023830112156101d0578160040135916106f7836105df565b926040610706815195866104c9565b818552602093808587019360051b850101938885116101d057818101935b85851061075757505050505050509261073c83610663565b9260a4359182116101d05761075391600401610674565b9091565b84358b81116101d05782019060a0828c0360231901126101d05784519061077d8261043d565b848301358252604483013560028110156101d057898301526064830135868301526084830135606083015260a4830135918d83116101d0576107c68d878c969587960101610605565b6080820152815201940193610724565b50346101d0576107e5366106a4565b6107f1949192946117df565b61081161080b61080184806126af565b60608101906126c5565b906126fa565b92600361081d8561273e565b6108268161272c565b141580610ac9575b610ab75760209161084f610843848701611762565b6001600160a01b031690565b9361097461095a61087d6108438761087761080b61086d88806126af565b6040810190612748565b01611762565b986001600160a01b039887906108bf7f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc8c166108b9818d61296a565b8d611dee565b60036108ca8261273e565b6108d38161272c565b03610aa15760400135809a5b604051627eeac760e11b808252306004830152602482018490529094918c16918f8587604481875afa968715610a94575b600097610a6e575b5060406109259101611948565b15610a5d576109369030908a6134be565b60405190815230600482015260248101929092529093849190829081906044820190565b03915afa918215610a50575b600092610a21575b506118d8565b91826109ac575b6109a28861099d8b6109988b8b6109938c8501611762565b612a37565b611762565b612818565b6100196001600255565b60005b8181106109bc575061097b565b80610a1b8a6109d66109d1600195878b6118ed565b6127b7565b8051610a15906109f5908a908d906001600160a01b031694015161190b565b610a0f610a038d8b01612a2d565b6001600160781b031690565b9061191e565b90612805565b016109af565b610a42919250873d8911610a49575b610a3a81836104c9565b810190611a09565b903861096e565b503d610a30565b610a58611a18565b610966565b610a699030908a6131b3565b610936565b610925919750610a8c604091883d8a11610a4957610a3a81836104c9565b979150610918565b610a9c611a18565b610910565b506060610aad8361277d565b510151809a6108df565b604051635863f78960e01b8152600490fd5b506005610ad58561273e565b610ade8161272c565b141561082e565b60009103126101d057565b50346101d057600080600319360112610b57576001546001600160a01b038116903382900361032f5782546001600160a01b03199081168317845516600155807f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b80fd5b60e06003198201126101d0576001600160401b03906004358281116101d05781610b86916004016101a0565b9093909260a06023198401126101d05760249260c4359182116101d05761075391600401610674565b50346101d057610bf1610bc136610b5a565b91610bce959394956117df565b602081013594610bdd86610340565b606082013596610bec88610340565b6120b4565b6040516370a0823160e01b8152306004820152906020826024816001600160a01b0387165afa918215610c68575b600092610c48575b5081610c37576100196001600255565b610c4092611bda565b3880806109a2565b610c6191925060203d8111610a4957610a3a81836104c9565b9038610c27565b610c70611a18565b610c1f565b50346101d057610c84366106a4565b610c929492949391936117df565b610ca261080b61080185806126af565b936002610cae8661273e565b610cb78161272c565b141580610e62575b610ab757602091610cd4610843848801611762565b93610cec6108438561087761080b61086d8b806126af565b966001600160a01b0396610d2c7f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc8916610d26818a61296a565b8a611dee565b610d3860408b01611948565b15610e5157610d49908330916134be565b6002610d548261273e565b610d5d8161272c565b03610e3c57604091500135945b6040516331a9108f60e11b8152600481018790529084826024818985165afa918215610e2f575b600092610e00575b5030911603610dc0575b6109a28661099d896109988989610dbb8a8501611762565b612886565b60005b818110610dd05750610da3565b80610dfa88610de56109d160019587896118ed565b805190880151906001600160a01b0316612805565b01610dc3565b610e21919250853d8711610e28575b610e1981836104c9565b8101906127a2565b9038610d99565b503d610e0f565b610e37611a18565b610d91565b50610e4860609161277d565b51015194610d6a565b610e5d908330916131b3565b610d49565b506004610e6e8661273e565b610e778161272c565b1415610cbf565b60809060231901126101d057602490565b5060c03660031901126101d0576001600160401b036004358181116101d057610ebc9036906004016105d1565b610ec536610e7e565b9160a4359081116101d057610ede903690600401610674565b929091610ee96117df565b60208083013593610ef985610340565b60608401359580610f3257505050610f12929350611983565b4780610f22576100196001600255565b610f2b916119ca565b38806109a2565b919392610f7491938747926040830135610f4b8161193e565b15610fd757610f6d92610f5c611952565b903591610f6883610340565b6135e4565b47906118d8565b60005b828110610f8a5750505050509050610f12565b80610fad88610fa88589610fa16001978a8c6118ed565b013561190b565b61191e565b80610fba575b5001610f77565b610fd190610fcc61099884888a6118ed565b6119ca565b38610fb3565b610ff492610fe3611952565b903591610fef83610340565b613223565b610f6d565b60c06003198201126101d0576001600160401b03906004358281116101d05781611025916004016101a0565b9093909260806023198401126101d05760249260a4359182116101d05761075391600401610674565b5061105836610ff9565b936110649391936117df565b6020808401359461107486610340565b6060850135968061108d57505050610f12939450612011565b929194939093479261109d611952565b6110a960408401611948565b156111585760005b84811061111a5750505050506110c89047906118d8565b60005b8281106110de5750505050509050610f12565b806110f588610fa88589610fa16001978a8c6118ed565b80611102575b50016110cb565b61111490610fcc61099884888a6118ed565b386110fb565b8061115261113461112e6001948988611fcc565b80611ffc565b61113d87611762565b858d61114a868c8b611fcc565b0135926135e4565b016110b1565b60005b8481106111705750505050506110c890610f6d565b806111a261118461112e6001948988611fcc565b61118d87611762565b858d61119a868c8b611fcc565b013592613223565b0161115b565b50346101d05760e03660031901126101d0576001600160401b036004358181116101d0576111da9036906004016105d1565b906111e4366105c0565b9060c4359081116101d057611200610bf1913690600401610674565b906112096117df565b60208401359361121885610340565b60608101359561122787610340565b611a25565b50346101d05760003660031901126101d0576000546040516001600160a01b039091168152602090f35b50346101d057610bf161126836610b5a565b91611275959394956117df565b60208101359461128486610340565b60608201359661129388610340565b6124bd565b50346101d057604060031981813601126101d0576001600160401b03906004358281116101d0576112cd9036906004016101a0565b90926024359081116101d057916112e9859336906004016101a0565b916112f26117df565b8451958694632a05d10160e21b8652806044870188600489015252606486019160648260051b8801019781936000925b8484106113b75789600081806113438f8e8e8e858403016024860152612f89565b0381837f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af180156113aa575b611387576100196001600255565b6113a3903d806000833e61139b81836104c9565b810190612b36565b50806109a2565b6113b2611a18565b611379565b919395969798509193986114066001916063198d82030185526114136113dd8d86612f02565b916113f76113eb8480612c3c565b89835289830190612dfd565b90602094848680960190612ed1565b91858185039101526117be565b9b019301940191938a989796959391611322565b5061143136610ff9565b9361143d9391936117df565b6020808401359461144d86610340565b6060850135968061146657505050610f12939450612410565b9294909391944792611476611952565b90604093611485858201611948565b156115625760005b898382106114f957505050505050506114a79047906118d8565b60005b8281106114bd5750505050509050610f12565b806114d488610fa88589610fa16001978a8c6118ed565b806114e1575b50016114aa565b6114f390610fcc61099884888a6118ed565b386114da565b9061155c61154687808888611556898e61154c8a8561153e60019e6115358461152d611527828f8890612279565b806122a9565b9b909d612279565b908101906122a9565b9b9095611762565b99612279565b0135963691612360565b9261393d565b0161148d565b60005b8983821061157d57505050505050506114a790610f6d565b906115b1611546878088886115ab898e61154c8a8561153e60019e6115358461152d611527828f8890612279565b92613857565b01611565565b50346101d05760003660031901126101d0576040517f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03168152602090f35b50346101d05760003660031901126101d0576001546040516001600160a01b039091168152602090f35b50346101d05760a03660031901126101d057611644600435610340565b61164f602435610340565b6084356001600160401b0381116101d05761166e90369060040161035e565b80611686575b60405163f23a6e6160e01b8152602090f35b611695916103f9913691610534565b3880611674565b50346101d05760203660031901126101d0576004356116ba81610340565b6000546001600160a01b0391908216330361032f57166bffffffffffffffffffffffff60a01b6001541617600155600080f35b50346101d05760003660031901126101d0576040517f0000000000000000000000001aed60a97192157fda7fb26267a439d523d09c5e6001600160a01b03168152602090f35b50634e487b7160e01b600052603260045260246000fd5b919081101561175a5760051b0190565b610600611733565b3561176c81610340565b90565b91908110156117b1575b60051b81013590601e19813603018212156101d05701908135916001600160401b0383116101d05760200182360381136101d0579190565b6117b9611733565b611779565b908060209392818452848401376000828201840152601f01601f1916010190565b60028054146117ee5760028055565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b3d1561185e573d906118448261050a565b9161185260405193846104c9565b82523d6000602084013e565b606090565b8151600092839260209091019083906001600160a01b03165af1611885611833565b501561188d57565b6040516322092f2f60e11b8152600490fd5b8151600093849391926020909201916001600160a01b03165af1611885611833565b50634e487b7160e01b600052601160045260246000fd5b919082039182116118e557565b61035c6118c1565b91908110156118fe575b60061b0190565b611906611733565b6118f7565b818102929181159184041417156118e557565b8115611928570490565b634e487b7160e01b600052601260045260246000fd5b801515036101d057565b3561176c8161193e565b604051602081018181106001600160401b03821117611976575b6040526000815290565b61197e610426565b61196c565b60408201356119918161193e565b156119b05761035c916119a2611952565b606082359261114a84610340565b61035c916119bc611952565b606082359261119a84610340565b816119d3575050565b6000918291829182916001600160a01b03165af16119ef611833565b50156119f757565b60405163d2dcf4f360e01b8152600490fd5b908160209103126101d0575190565b506040513d6000823e3d90fd5b9091939293611a3660608401611762565b60808401359580611a4f5750505061035c929350611b46565b6040516370a0823160e01b808252306004830152602096949593949293611abe936001600160a01b038716939289929091908385602481895afa948515611b39575b600095611b14575b5090611aa491611b46565b60405190815230600482015292839081806024810161095a565b60005b828110611ad2575050505050509050565b80611ae989610fa8858a610fa16001978a8d6118ed565b80611af6575b5001611ac1565b611b0e90611b0861099884888b6118ed565b87611bda565b38611aef565b611aa492919550611b3190853d8711610a4957610a3a81836104c9565b949091611a99565b611b41611a18565b611a91565b611b8d6060830135611b5781610340565b6080840135907f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b031690611ee0565b6040820135611b9b8161193e565b15611bbd5761035c91611bac611952565b903591611bb883610340565b6134be565b61035c91611bc9611952565b903591611bd583610340565b6131b3565b60405163a9059cbb60e01b60208083019182526001600160a01b039490941660248301526044808301959095529381529192611c7392916000908190611c216064866104c9565b60018060a01b03169260405194611c3786610465565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488870152519082855af1611c6d611833565b91611d5c565b805190828215928315611ce3575b50505015611c8c5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b611cf39350820181019101611cfb565b388281611c81565b908160209103126101d0575161176c8161193e565b15611d1757565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b91929015611d7c5750815115611d70575090565b61176c903b1515611d10565b825190915015611d8f5750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b828510611dd5575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350611db2565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526020939216908381604481855afa908115611ed3575b600091611eb6575b5060001911611e3d57505050565b60405163095ea7b360e01b81526001600160a01b0392909216600483015260001960248301528290829060449082906000905af18015611ea9575b611e80575050565b81611e9f92903d10611ea2575b611e9781836104c9565b810190611cfb565b50565b503d611e8d565b611eb1611a18565b611e78565b611ecd9150843d8611610a4957610a3a81836104c9565b38611e2f565b611edb611a18565b611e27565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152602094921692919081908581604481885afa908115611fbf575b600091611fa2575b5010611f32575b50505050565b60405163095ea7b360e01b81526001600160a01b039290921660048301526024820152908290829060449082906000905af18015611f95575b611f77575b8080611f2c565b81611f8d92903d10611ea257611e9781836104c9565b503880611f70565b611f9d611a18565b611f6b565b611fb99150863d8811610a4957610a3a81836104c9565b38611f25565b611fc7611a18565b611f1d565b9190811015611fef575b60051b81013590603e19813603018212156101d0570190565b611ff7611733565b611fd6565b903590609e19813603018212156101d0570190565b90929161201c611952565b9161202960408301611948565b156120715760005b85811061204057505050509050565b8061206b61205461112e6001948a87611fcc565b61205d86611762565b87602061114a868d8a611fcc565b01612031565b60005b85811061208357505050509050565b806120ae61209761112e6001948a87611fcc565b6120a086611762565b87602061119a868d8a611fcc565b01612074565b9091929493946120c660608501611762565b608085013596806120df5750505061035c9394506121de565b6040516370a0823160e01b808252306004830152602097949693959294612134946001600160a01b038816948a9392919084866024818a5afa9586156121ab575b600096612184575b5090611aa492916121de565b60005b828110612148575050505050509050565b8061215f89610fa8858a610fa16001978a8d6118ed565b8061216c575b5001612137565b61217e90611b0861099884888b6118ed565b38612165565b611aa493929196506121a290863d8811610a4957610a3a81836104c9565b95909192612128565b6121b3611a18565b612120565b909161176c928110156121d1575b60051b810190611ffc565b6121d9611733565b6121c6565b91906121ef611b5760608401611762565b6121f7611952565b9061220460408401611948565b156122415760005b81811061221a575050505050565b8061223b61222b60019385896121b8565b8561223588611762565b916134be565b0161220c565b60005b818110612252575050505050565b8061227361226360019385896121b8565b8561226d88611762565b916131b3565b01612244565b919081101561229c575b60051b81013590605e19813603018212156101d0570190565b6122a4611733565b612283565b903590601e19813603018212156101d057018035906001600160401b0382116101d057602001918160051b360383136101d057565b81601f820112156101d0578035906122f5826105df565b92604092612305845195866104c9565b808552602091828087019260061b850101938185116101d0578301915b8483106123325750505050505090565b85838303126101d057838691825161234981610465565b853581528286013583820152815201920191612322565b9092919261236d816105df565b9160409161237d835194856104c9565b839581855260208095019160051b8301938185116101d05783925b8584106123a85750505050505050565b6001600160401b039084358281116101d05786019083828603126101d05783516123d181610465565b82358481116101d057866123e69185016122de565b8152898301359384116101d057612401868b958695016122de565b83820152815201930192612398565b90612419611952565b604093612427858201611948565b156124825760005b83811061243e57505050505050565b8061247c61154687878785611556898e61154c61246161152760019d898b612279565b95909761153e61247283838d612279565b60208101906122a9565b0161242f565b60005b83811061249457505050505050565b806124b7611546878787856115ab898e61154c61246161152760019d898b612279565b01612485565b9091929493946124cf60608501611762565b608085013596806124e85750505061035c9394506125c1565b6040516370a0823160e01b80825230600483015260209794969395929461253d946001600160a01b038816948a9392919084866024818a5afa9586156125b4575b60009661258d575b5090611aa492916125c1565b60005b828110612551575050505050509050565b8061256889610fa8858a610fa16001978a8d6118ed565b80612575575b5001612540565b61258790611b0861099884888b6118ed565b3861256e565b611aa493929196506125ab90863d8811610a4957610a3a81836104c9565b95909192612531565b6125bc611a18565b612529565b906126076125d160608501611762565b6080850135907f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b031690611ee0565b61260f611952565b9061261c60408501611948565b1561267a5760005b818110612632575050505050565b8061267461266e8561264a611527600196888b612279565b61265b612472878a8d969596612279565b94906126668d611762565b953691612360565b926138b3565b01612624565b60005b81811061268b575050505050565b806126a96126a38561264a611527600196888b612279565b926137eb565b0161267d565b90359061015e19813603018212156101d0570190565b903590601e19813603018212156101d057018035906001600160401b0382116101d0576020019160c08202360383136101d057565b90156127035790565b61176c611733565b600611156101d057565b50634e487b7160e01b600052602160045260246000fd5b6006111561273657565b61035c612715565b3561176c8161270b565b903590601e19813603018212156101d057018035906001600160401b0382116101d0576020019160a08202360383136101d057565b60209080511561278b570190565b612793611733565b0190565b519061035c82610340565b908160209103126101d0575161176c81610340565b6040813603126101d057602060405191604083018381106001600160401b038211176127f8575b60405280356127ec81610340565b83520135602082015290565b612800610426565b6127de565b8161280f57505050565b61035c92611bda565b6040516370a0823160e01b8152306004820152906020826024816001600160a01b0387165afa918215612879575b600092612859575b508161280f57505050565b61287291925060203d8111610a4957610a3a81836104c9565b903861284e565b612881611a18565b612846565b6040516331a9108f60e11b8152600481018490526001600160a01b03928316939192602082602481885afa91821561295d575b60009261293d575b501630146128ce57505050565b823b156101d057604051632142170760e11b81523060048201526001600160a01b0390921660248301526044820152906000908290818381606481015b03925af18015612930575b61291d5750565b8061292a61035c92610480565b80610ae5565b612938611a18565b612916565b61295691925060203d8111610e2857610e1981836104c9565b90386128c1565b612965611a18565b6128b9565b60405163e985e9c560e01b81523060048201526001600160a01b0383811660248301529190911690602081604481855afa908115612a0f575b6000916129f1575b50156129b5575050565b803b156101d05760405163a22cb46560e01b81526001600160a01b0390921660048301526001602483015260009082908183816044810161290b565b612a09915060203d8111611ea257611e9781836104c9565b386129ab565b612a17611a18565b6129a3565b6001600160781b038116036101d057565b3561176c81612a1c565b604051627eeac760e11b81523060048201526024810184905290916001600160a01b0316602082604481845afa918215612b29575b600092612b09575b5081612a805750505050565b803b156101d057604051637921219560e11b81523060048201526001600160a01b039390931660248401526044830193909352606482015260a06084820152600060a482018190529091829060c490829084905af18015612afc575b612ae9575b808080611f2c565b8061292a612af692610480565b38612ae1565b612b04611a18565b612adc565b612b2291925060203d8111610a4957610a3a81836104c9565b9038612a74565b612b31611a18565b612a6c565b60209081818403126101d0578051906001600160401b0382116101d0570182601f820112156101d057805191612b6b836105df565b936040612b7a815196876104c9565b848652828601918360e0809702860101948186116101d0578401925b858410612ba7575050505050505090565b8382038781126101d057835191612bbd83610493565b60a08092126101d0578892612c2488938751612bd88161043d565b8951612be38161270b565b8152858a0151612bf281610340565b86820152888a0151898201526060808b0151908201526080808b015190612c1882610340565b82015283528801612797565b8382015260c087015186820152815201930192612b96565b903561015e19823603018112156101d0570190565b9035601e19823603018112156101d05701602081359101916001600160401b0382116101d05760a08202360383136101d057565b906006821015612c925752565b612c9a612715565b52565b9190808252602080920192916000905b828210612cbb575050505090565b9091929380612cd66001928735612cd18161270b565b612c85565b82860135612ce381610340565b828060a01b03168382015260408087013590820152606080870135908201526080808701359082015260a08091019501920190929192612cad565b9035601e19823603018112156101d05701602081359101916001600160401b0382116101d05760c08202360383136101d057565b9190808252602080920192916000905b828210612d70575050505090565b9091929380612d866001928735612cd18161270b565b82860135612d9381610340565b828060a01b038091168483015260408088013590830152606080880135908301526080808801359083015260a09081880135612dce81610340565b169082015260c0908101950193920190612d62565b359060048210156101d057565b906004821015612c925752565b90612e1881612e0b84610351565b6001600160a01b03169052565b612e37612e2760208401610351565b6001600160a01b03166020830152565b612e76612e5b612e4a6040850185612c51565b610160806040870152850191612c9d565b612e686060850185612d1e565b908483036060860152612d52565b91612e90612e8660808301612de3565b6080840190612df0565b60a081013560a083015260c081013560c083015260e081013560e0830152610100808201359083015261012080820135908301526101408091013591015290565b9035601e19823603018112156101d05701602081359101916001600160401b0382116101d05781360383136101d057565b9035603e19823603018112156101d0570190565b9035601e19823603018112156101d05701602081359101916001600160401b0382116101d0578160061b360383136101d057565b9190808252602080920192916000905b828210612f68575050505090565b83358552838101358582015260409485019490930192600190910190612f5a565b90808352602080930192838260051b850194846000925b858410612fb1575050505050505090565b9091929394959685806130048385600195038852612fcf8c88612f02565b90612ff7612fed612fe08480612f16565b6040808652850191612f4a565b9285810190612f16565b9185818503910152612f4a565b990194019401929594939190612fa0565b61176c9161309261308761303a61302c8580612c3c565b60a0855260a0850190612dfd565b602085013561304881612a1c565b6001600160781b038091166020860152604086013561306681612a1c565b1660408501526130796060860186612ed1565b9085830360608701526117be565b926080810190612ed1565b9160808185039101526117be565b90815180825260208092019182818360051b8201950193600080925b8584106130cd575050505050505090565b9091929394959681810384528751908660c060a0928381019385518252838601516002811015613165575b8483015260408087015190830152606080870151908301526080958601519582015284519384905291939101919083019085905b80821061314c5750505090806001929901940194019295949391906130bc565b919380600192948651815201940192018893929161312c565b61316d612715565b6130f8565b9161319c9061318e606093969596608086526080860190613015565b9084820360208601526130a0565b600060408401526001600160a01b03909416910152565b906020916131d560405194859384936339eb2ac960e21b855260048501613172565b038160007f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af161320c5750565b611e9f9060203d8111611ea257611e9781836104c9565b926020929161324994604051958694859384936339eb2ac960e21b855260048501613172565b03917f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af161320c5750565b81601f820112156101d057803590613295826105df565b926040906132a5825195866104c9565b838552602091828601918360a0809702860101948186116101d0578401925b8584106132d5575050505050505090565b86848303126101d05784879184516132ec8161043d565b86356132f78161270b565b81528287013561330681610340565b83820152858701358682015260608088013590820152608080880135908201528152019301926132c4565b81601f820112156101d057803590613348826105df565b92604090613358825195866104c9565b838552602091828601918360c0809702860101948186116101d0578401925b858410613388575050505050505090565b86848303126101d057848791845161339f816104ae565b86356133aa8161270b565b8152828701356133b981610340565b838201528587013586820152606080880135908201526080808801359082015260a080880135906133e982610340565b820152815201930192613377565b610160813603126101d05761340a6104ea565b9061341481610351565b825261342260208201610351565b60208301526001600160401b0360408201358181116101d057613448903690840161327e565b604084015260608201359081116101d0576134669036908301613331565b606083015261347760808201612de3565b608083015260a081013560a083015260c081013560c083015260e081013560e083015261010080820135908301526101208082013590830152610140809101359082015290565b916134d96134d46134cf85806126af565b6133f7565b613bc0565b91613519604085019160206134f96134f3610a0386612a2d565b87613cb5565b9460009260405194859283926339eb2ac960e21b84528b60048501613172565b0381847f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af19091816135c4575b5061356757604051631298f31b60e11b8152600490fd5b61357d57604051631298f31b60e11b8152600490fd5b6135a4610a039261359f6135ab95613599610a03602096612a2d565b90613cb5565b6118d8565b9301612a2d565b036135b257565b604051631298f31b60e11b8152600490fd5b6135dd91925060203d8111611ea257611e9781836104c9565b9038613550565b9290916136386135fa6134d46134cf87806126af565b9360206040870193613617613611610a0387612a2d565b88613cb5565b956000936040518096819482936339eb2ac960e21b84528d60048501613172565b03917f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af19091816135c4575061356757604051631298f31b60e11b8152600490fd5b90815180825260208080930193019160005b8281106136a4575050505090565b8351805186528201518583015260409094019392810192600101613696565b90815180825260208092019182818360051b82019501936000915b8483106136ee5750505050505090565b909192939495848061372783856001950387528a51908361371783516040808552840190613684565b9201519084818403910152613684565b98019301930191949392906136de565b949290959391958660808701608088525260a086019660a08160051b8801019782600090815b8483106137a85750505050505060609161378787613795938861035c98999a0360208b01526130a0565b9087820360408901526136c3565b6001600160a01b03909216940193909352565b90919293949a609f198b82030185528b35609e19843603018112156137e757600191846137d59201613015565b9b60209081019695019301919061375d565b8280fd5b604051637968958960e11b8152946000948694859461380d9460048701613737565b0381837f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af16138435750565b611e9f903d806000833e61139b81836104c9565b61387e6000959693949660405197889687958695637968958960e11b875260048701613737565b03917f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af16138435750565b604051637968958960e11b815294600094869485946138d59460048701613737565b0381837f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af19081613922575b5061035c57604051631298f31b60e11b8152600490fd5b613936903d806000833e61139b81836104c9565b503861390b565b6139646000959693949660405197889687958695637968958960e11b875260048701613737565b03917f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165af19081613922575061035c57604051631298f31b60e11b8152600490fd5b6040519061016082018281106001600160401b03821117613a15575b60405281610140600091828152826020820152606060408201526060808201528260808201528260a08201528260c08201528260e082015282610100820152826101208201520152565b613a1d610426565b6139cb565b90815180825260208080930193019160005b828110613a42575050505090565b909192938260a06001928751613a59828251612c85565b8084015185841b869003168285015260408082015190830152606080820151908301526080908101519082015201950193929101613a34565b90815180825260208080930193019160005b828110613ab2575050505090565b909192938260c06001928751613ac9828251612c85565b848060a01b038085830151168584015260408083015190840152606080830151908401526080808301519084015260a0809201511690820152019501910192919092613aa4565b602080825282516001600160a01b03169082015260208201516001600160a01b031660408201526040820151613b6d613b5761016092836060860152610180850190613a22565b6060850151848203601f19016080860152613a92565b92613b80608082015160a0850190612df0565b60a081015160c084015260c081015160e084015260e081015161010090818501528101516101209081850152810151906101409182850152015191015290565b613bc86139af565b50805160405163f07ec37360e01b81526001600160a01b039182166004820152602092613c509284927f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc909116908381602481855afa908115613ca8575b600091613c8b575b5061014083015260405180809581946379df72bd60e01b835260048301613b10565b03915afa918215613c7e575b600092613c6857505090565b61176c9250803d10610a4957610a3a81836104c9565b613c86611a18565b613c5c565b613ca29150843d8611610a4957610a3a81836104c9565b38613c2e565b613cb0611a18565b613c26565b6040516346423aa760e01b815260048101919091526080816024817f00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc6001600160a01b03165afa908115613d7f575b6000908192613d2c575b5060008215613d25575061176c92610fa89161190b565b9250505090565b91506080823d8211613d77575b81613d46608093836104c9565b81010312610b575750613d59815161193e565b613d66602082015161193e565b606060408201519101519038613d0e565b3d9150613d39565b613d87611a18565b613d0456fea2646970667358221220476b64bb30b77ad423a331edf7298001176966c712f6569bbdb76b003767694764736f6c63430008110033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000f3d63166f0ca56c3c1a3508fce03ff0cf3fb691e0000000000000000000000001aed60a97192157fda7fb26267a439d523d09c5e00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc

-----Decoded View---------------
Arg [0] : owner (address): 0xf3d63166F0Ca56C3c1A3508FcE03Ff0Cf3Fb691e
Arg [1] : router (address): 0x1aeD60A97192157fDA7fb26267A439d523d09c5e
Arg [2] : exchange (address): 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000f3d63166f0ca56c3c1a3508fce03ff0cf3fb691e
Arg [1] : 0000000000000000000000001aed60a97192157fda7fb26267a439d523d09c5e
Arg [2] : 00000000000000000000000000000000000000adc04c56bf30ac9d3c0aaf14dc


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.