ETH Price: $3,824.39 (+4.67%)

Contract

0x99573fFc2837ed9C9CC55E4905aBf8D8d8342E15
 

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

Please try again later

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
147187842022-05-05 18:27:47951 days ago1651775267  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FNDNFTMarket

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 1337 runs

Other Settings:
default evmVersion
File 1 of 29 : FNDNFTMarket.sol
/*
  ・
   * ★
      ・ 。
         ・ ゚☆ 。
      * ★ ゚・。 *  。
            * ☆ 。・゚*.。
         ゚ *.。☆。★ ・
​
                      `                     .-:::::-.`              `-::---...```
                     `-:`               .:+ssssoooo++//:.`       .-/+shhhhhhhhhhhhhyyyssooo:
                    .--::.            .+ossso+/////++/:://-`   .////+shhhhhhhhhhhhhhhhhhhhhy
                  `-----::.         `/+////+++///+++/:--:/+/-  -////+shhhhhhhhhhhhhhhhhhhhhy
                 `------:::-`      `//-.``.-/+ooosso+:-.-/oso- -////+shhhhhhhhhhhhhhhhhhhhhy
                .--------:::-`     :+:.`  .-/osyyyyyyso++syhyo.-////+shhhhhhhhhhhhhhhhhhhhhy
              `-----------:::-.    +o+:-.-:/oyhhhhhhdhhhhhdddy:-////+shhhhhhhhhhhhhhhhhhhhhy
             .------------::::--  `oys+/::/+shhhhhhhdddddddddy/-////+shhhhhhhhhhhhhhhhhhhhhy
            .--------------:::::-` +ys+////+yhhhhhhhddddddddhy:-////+yhhhhhhhhhhhhhhhhhhhhhy
          `----------------::::::-`.ss+/:::+oyhhhhhhhhhhhhhhho`-////+shhhhhhhhhhhhhhhhhhhhhy
         .------------------:::::::.-so//::/+osyyyhhhhhhhhhys` -////+shhhhhhhhhhhhhhhhhhhhhy
       `.-------------------::/:::::..+o+////+oosssyyyyyyys+`  .////+shhhhhhhhhhhhhhhhhhhhhy
       .--------------------::/:::.`   -+o++++++oooosssss/.     `-//+shhhhhhhhhhhhhhhhhhhhyo
     .-------   ``````.......--`        `-/+ooooosso+/-`          `./++++///:::--...``hhhhyo
                                              `````
   * 
      ・ 。
    ・  ゚☆ 。
      * ★ ゚・。 *  。
            * ☆ 。・゚*.。
         ゚ *.。☆。★ ・
    *  ゚。·*・。 ゚*
     ☆゚・。°*. ゚
  ・ ゚*。・゚★。
  ・ *゚。   *
 ・゚*。★・
 ☆∴。 *
・ 。
*/

// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "./mixins/Constants.sol";
import "./mixins/FoundationTreasuryNode.sol";
import "./mixins/NFTMarketAuction.sol";
import "./mixins/NFTMarketBuyPrice.sol";
import "./mixins/NFTMarketCore.sol";
import "./mixins/NFTMarketCreators.sol";
import "./mixins/NFTMarketFees.sol";
import "./mixins/NFTMarketOffer.sol";
import "./mixins/NFTMarketPrivateSale.sol";
import "./mixins/NFTMarketReserveAuction.sol";
import "./mixins/SendValueWithFallbackWithdraw.sol";

/**
 * @title A market for NFTs on Foundation.
 * @notice The Foundation marketplace is a contract which allows traders to buy and sell NFTs.
 * It supports buying and selling via auctions, private sales, buy price, and offers.
 * @dev All sales in the Foundation market will pay the creator 10% royalties on secondary sales. This is not specific
 * to NFTs minted on Foundation, it should work for any NFT. If royalty information was not defined when the NFT was
 * originally deployed, it may be added using the [Royalty Registry](https://royaltyregistry.xyz/) which will be
 * respected by our market contract.
 */
contract FNDNFTMarket is
  Constants,
  Initializable,
  FoundationTreasuryNode,
  NFTMarketCore,
  ReentrancyGuardUpgradeable,
  NFTMarketCreators,
  SendValueWithFallbackWithdraw,
  NFTMarketFees,
  NFTMarketAuction,
  NFTMarketReserveAuction,
  NFTMarketPrivateSale,
  NFTMarketBuyPrice,
  NFTMarketOffer
{
  /**
   * @notice Set immutable variables for the implementation contract.
   * @dev Using immutable instead of constants allows us to use different values on testnet.
   * @param treasury The Foundation Treasury contract address.
   * @param feth The FETH ERC-20 token contract address.
   * @param royaltyRegistry The Royalty Registry contract address.
   * @param duration The duration of the auction in seconds.
   * @param marketProxyAddress The address of the proxy fronting this contract.
   */
  constructor(
    address payable treasury,
    address feth,
    address royaltyRegistry,
    uint256 duration,
    address marketProxyAddress
  )
    FoundationTreasuryNode(treasury)
    NFTMarketCore(feth)
    NFTMarketCreators(royaltyRegistry)
    NFTMarketReserveAuction(duration)
    NFTMarketPrivateSale(marketProxyAddress) // solhint-disable-next-line no-empty-blocks
  {}

  /**
   * @notice Called once to configure the contract after the initial proxy deployment.
   * @dev This farms the initialize call out to inherited contracts as needed to initialize mutable variables.
   */
  function initialize() external initializer {
    NFTMarketAuction._initializeNFTMarketAuction();
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev This is a no-op function required to avoid compile errors.
   */
  function _beforeAuctionStarted(address nftContract, uint256 tokenId)
    internal
    override(NFTMarketCore, NFTMarketBuyPrice, NFTMarketOffer)
  {
    super._beforeAuctionStarted(nftContract, tokenId);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev This is a no-op function required to avoid compile errors.
   */
  function _transferFromEscrow(
    address nftContract,
    uint256 tokenId,
    address recipient,
    address authorizeSeller
  ) internal override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) {
    super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev This is a no-op function required to avoid compile errors.
   */
  function _transferFromEscrowIfAvailable(
    address nftContract,
    uint256 tokenId,
    address recipient
  ) internal override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) {
    super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev This is a no-op function required to avoid compile errors.
   */
  function _transferToEscrow(address nftContract, uint256 tokenId)
    internal
    override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice)
  {
    super._transferToEscrow(nftContract, tokenId);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev This is a no-op function required to avoid compile errors.
   */
  function _getSellerFor(address nftContract, uint256 tokenId)
    internal
    view
    override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice)
    returns (address payable seller)
  {
    seller = super._getSellerFor(nftContract, tokenId);
  }
}

File 2 of 29 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 3 of 29 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ReentrancyGuardUpgradeable is Initializable {
    // 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;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 4 of 29 : Constants.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

/**
 * @title Constant values shared across mixins.
 */
abstract contract Constants {
  /**
   * @notice 100% in basis points.
   */
  uint256 internal constant BASIS_POINTS = 10000;

  /**
   * @notice Cap the number of royalty recipients.
   * @dev A cap is required to ensure gas costs are not too high when a sale is settled.
   */
  uint256 internal constant MAX_ROYALTY_RECIPIENTS = 5;

  /**
   * @notice The minimum increase of 10% required when making an offer or placing a bid.
   */
  uint256 internal constant MIN_PERCENT_INCREMENT_DENOMINATOR = BASIS_POINTS / 1000;

  /**
   * @notice The gas limit used when making external read-only calls.
   * @dev This helps to ensure that external calls does not prevent the market from executing.
   */
  uint256 internal constant READ_ONLY_GAS_LIMIT = 40000;

  /**
   * @notice The gas limit to send ETH to multiple recipients, enough for a 5-way split.
   */
  uint256 internal constant SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS = 210000;

  /**
   * @notice The gas limit to send ETH to a single recipient, enough for a contract with a simple receiver.
   */
  uint256 internal constant SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT = 20000;
}

File 5 of 29 : FoundationTreasuryNode.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

import "../interfaces/IAdminRole.sol";
import "../interfaces/IOperatorRole.sol";

error FoundationTreasuryNode_Address_Is_Not_A_Contract();
error FoundationTreasuryNode_Caller_Not_Admin();
error FoundationTreasuryNode_Caller_Not_Operator();

/**
 * @title A mixin that stores a reference to the Foundation treasury contract.
 * @notice The treasury collects fees and defines admin/operator roles.
 */
abstract contract FoundationTreasuryNode is Initializable {
  using AddressUpgradeable for address payable;

  /// @dev This value was replaced with an immutable version.
  address payable private __gap_was_treasury;

  /// @notice The address of the treasury contract.
  address payable private immutable treasury;

  /// @notice Requires the caller is a Foundation admin.
  modifier onlyFoundationAdmin() {
    if (!IAdminRole(treasury).isAdmin(msg.sender)) {
      revert FoundationTreasuryNode_Caller_Not_Admin();
    }
    _;
  }

  /// @notice Requires the caller is a Foundation operator.
  modifier onlyFoundationOperator() {
    if (!IOperatorRole(treasury).isOperator(msg.sender)) {
      revert FoundationTreasuryNode_Caller_Not_Operator();
    }
    _;
  }

  /**
   * @notice Set immutable variables for the implementation contract.
   * @dev Assigns the treasury contract address.
   */
  constructor(address payable _treasury) {
    if (!_treasury.isContract()) {
      revert FoundationTreasuryNode_Address_Is_Not_A_Contract();
    }
    treasury = _treasury;
  }

  /**
   * @notice Gets the Foundation treasury contract.
   * @dev This call is used in the royalty registry contract.
   * @return treasuryAddress The address of the Foundation treasury contract.
   */
  function getFoundationTreasury() public view returns (address payable treasuryAddress) {
    return treasury;
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[2000] private __gap;
}

File 6 of 29 : NFTMarketAuction.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/**
 * @title An abstraction layer for auctions.
 * @dev This contract can be expanded with reusable calls and data as more auction types are added.
 */
abstract contract NFTMarketAuction is Initializable {
  /**
   * @notice A global id for auctions of any type.
   */
  uint256 private nextAuctionId;

  /**
   * @notice Called once to configure the contract after the initial proxy deployment.
   * @dev This sets the initial auction id to 1, making the first auction cheaper
   * and id 0 represents no auction found.
   */
  function _initializeNFTMarketAuction() internal onlyInitializing {
    nextAuctionId = 1;
  }

  /**
   * @notice Returns id to assign to the next auction.
   */
  function _getNextAndIncrementAuctionId() internal returns (uint256) {
    // AuctionId cannot overflow 256 bits.
    unchecked {
      return nextAuctionId++;
    }
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[1000] private __gap;
}

File 7 of 29 : NFTMarketBuyPrice.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "./NFTMarketCore.sol";
import "./NFTMarketFees.sol";

/// @param buyPrice The current buy price set for this NFT.
error NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price(uint256 buyPrice);
error NFTMarketBuyPrice_Cannot_Buy_Unset_Price();
error NFTMarketBuyPrice_Cannot_Cancel_Unset_Price();
/// @param owner The current owner of this NFT.
error NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price(address owner);
/// @param owner The current owner of this NFT.
error NFTMarketBuyPrice_Only_Owner_Can_Set_Price(address owner);
error NFTMarketBuyPrice_Price_Already_Set();
error NFTMarketBuyPrice_Price_Too_High();
/// @param seller The current owner of this NFT.
error NFTMarketBuyPrice_Seller_Mismatch(address seller);

/**
 * @title Allows sellers to set a buy price of their NFTs that may be accepted and instantly transferred to the buyer.
 * @notice NFTs with a buy price set are escrowed in the market contract.
 */
abstract contract NFTMarketBuyPrice is NFTMarketCore, NFTMarketFees {
  using AddressUpgradeable for address payable;

  /// @notice Stores the buy price details for a specific NFT.
  /// @dev The struct is packed into a single slot to optimize gas.
  struct BuyPrice {
    /// @notice The current owner of this NFT which set a buy price.
    /// @dev A zero price is acceptable so a non-zero address determines whether a price has been set.
    address payable seller;
    /// @notice The current buy price set for this NFT.
    uint96 price;
  }

  /// @notice Stores the current buy price for each NFT.
  mapping(address => mapping(uint256 => BuyPrice)) private nftContractToTokenIdToBuyPrice;

  /**
   * @notice Emitted when an NFT is bought by accepting the buy price,
   * indicating that the NFT has been transferred and revenue from the sale distributed.
   * @dev The total buy price that was accepted is `protocolFee` + `creatorFee` + `sellerRev`.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param buyer The address of the collector that purchased the NFT using `buy`.
   * @param seller The address of the seller which originally set the buy price.
   * @param protocolFee The amount of ETH that was sent to Foundation for this sale.
   * @param creatorFee The amount of ETH that was sent to the creator for this sale.
   * @param sellerRev The amount of ETH that was sent to the owner for this sale.
   */
  event BuyPriceAccepted(
    address indexed nftContract,
    uint256 indexed tokenId,
    address indexed seller,
    address buyer,
    uint256 protocolFee,
    uint256 creatorFee,
    uint256 sellerRev
  );
  /**
   * @notice Emitted when the buy price is removed by the owner of an NFT.
   * @dev The NFT is transferred back to the owner unless it's still escrowed for another market tool,
   * e.g. listed for sale in an auction.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   */
  event BuyPriceCanceled(address indexed nftContract, uint256 indexed tokenId);
  /**
   * @notice Emitted when a buy price is invalidated due to other market activity.
   * @dev This occurs when the buy price is no longer eligible to be accepted,
   * e.g. when a bid is placed in an auction for this NFT.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   */
  event BuyPriceInvalidated(address indexed nftContract, uint256 indexed tokenId);
  /**
   * @notice Emitted when a buy price is set by the owner of an NFT.
   * @dev The NFT is transferred into the market contract for escrow unless it was already escrowed,
   * e.g. for auction listing.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param seller The address of the NFT owner which set the buy price.
   * @param price The price of the NFT.
   */
  event BuyPriceSet(address indexed nftContract, uint256 indexed tokenId, address indexed seller, uint256 price);

  /**
   * @notice Buy the NFT at the set buy price.
   * `msg.value` must be <= `maxPrice` and any delta will be taken from the account's available FETH balance.
   * @dev `maxPrice` protects the buyer in case a the price is increased but allows the transaction to continue
   * when the price is reduced (and any surplus funds provided are refunded).
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param maxPrice The maximum price to pay for the NFT.
   */
  function buy(
    address nftContract,
    uint256 tokenId,
    uint256 maxPrice
  ) external payable {
    buyV2(nftContract, tokenId, maxPrice, payable(0));
  }

  /**
   * @notice Buy the NFT at the set buy price.
   * `msg.value` must be <= `maxPrice` and any delta will be taken from the account's available FETH balance.
   * @dev `maxPrice` protects the buyer in case a the price is increased but allows the transaction to continue
   * when the price is reduced (and any surplus funds provided are refunded).
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param maxPrice The maximum price to pay for the NFT.
   * @param referrer The address of the referrer.
   */
  function buyV2(
    address nftContract,
    uint256 tokenId,
    uint256 maxPrice,
    address payable referrer
  ) public payable {
    BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId];
    if (buyPrice.price > maxPrice) {
      revert NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price(buyPrice.price);
    } else if (buyPrice.seller == address(0)) {
      revert NFTMarketBuyPrice_Cannot_Buy_Unset_Price();
    }

    _buy(nftContract, tokenId, referrer);
  }

  /**
   * @notice Removes the buy price set for an NFT.
   * @dev The NFT is transferred back to the owner unless it's still escrowed for another market tool,
   * e.g. listed for sale in an auction.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   */
  function cancelBuyPrice(address nftContract, uint256 tokenId) external nonReentrant {
    address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller;
    if (seller == address(0)) {
      // This check is redundant with the next one, but done in order to provide a more clear error message.
      revert NFTMarketBuyPrice_Cannot_Cancel_Unset_Price();
    } else if (seller != msg.sender) {
      revert NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price(seller);
    }

    // Remove the buy price
    delete nftContractToTokenIdToBuyPrice[nftContract][tokenId];

    // Transfer the NFT back to the owner if it is not listed in auction.
    _transferFromEscrowIfAvailable(nftContract, tokenId, msg.sender);

    emit BuyPriceCanceled(nftContract, tokenId);
  }

  /**
   * @notice Sets the buy price for an NFT and escrows it in the market contract.
   * A 0 price is acceptable and valid price you can set, enabling a giveaway to the first collector that calls `buy`.
   * @dev If there is an offer for this amount or higher, that will be accepted instead of setting a buy price.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param price The price at which someone could buy this NFT.
   */
  function setBuyPrice(
    address nftContract,
    uint256 tokenId,
    uint256 price
  ) external nonReentrant {
    // If there is a valid offer at this price or higher, accept that instead.
    if (_autoAcceptOffer(nftContract, tokenId, price)) {
      return;
    }

    if (price > type(uint96).max) {
      // This ensures that no data is lost when storing the price as `uint96`.
      revert NFTMarketBuyPrice_Price_Too_High();
    }

    BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId];
    address seller = buyPrice.seller;

    if (buyPrice.price == price && seller != address(0)) {
      revert NFTMarketBuyPrice_Price_Already_Set();
    }

    // Store the new price for this NFT.
    buyPrice.price = uint96(price);

    if (seller == address(0)) {
      // Transfer the NFT into escrow, if it's already in escrow confirm the `msg.sender` is the owner.
      _transferToEscrow(nftContract, tokenId);

      // The price was not previously set for this NFT, store the seller.
      buyPrice.seller = payable(msg.sender);
    } else if (seller != msg.sender) {
      // Buy price was previously set by a different user
      revert NFTMarketBuyPrice_Only_Owner_Can_Set_Price(seller);
    }

    emit BuyPriceSet(nftContract, tokenId, msg.sender, price);
  }

  /**
   * @notice If there is a buy price at this price or lower, accept that and return true.
   */
  function _autoAcceptBuyPrice(
    address nftContract,
    uint256 tokenId,
    uint256 maxPrice
  ) internal override returns (bool) {
    BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId];
    if (buyPrice.seller == address(0) || buyPrice.price > maxPrice) {
      // No buy price was found, or the price is too high.
      return false;
    }

    _buy(nftContract, tokenId, payable(0));
    return true;
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Invalidates the buy price on a auction start, if one is found.
   */
  function _beforeAuctionStarted(address nftContract, uint256 tokenId) internal virtual override {
    BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId];
    if (buyPrice.seller != address(0)) {
      // A buy price was set for this NFT, invalidate it.
      _invalidateBuyPrice(nftContract, tokenId);
    }
    super._beforeAuctionStarted(nftContract, tokenId);
  }

  /**
   * @notice Process the purchase of an NFT at the current buy price.
   * @dev The caller must confirm that the seller != address(0) before calling this function.
   */
  function _buy(
    address nftContract,
    uint256 tokenId,
    address payable referrer
  ) private nonReentrant {
    BuyPrice memory buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId];

    // Remove the buy now price
    delete nftContractToTokenIdToBuyPrice[nftContract][tokenId];

    // Cancel the buyer's offer if there is one in order to free up their FETH balance
    // even if they don't need the FETH for this specific purchase.
    _cancelSendersOffer(nftContract, tokenId);

    if (buyPrice.price > msg.value) {
      // Withdraw additional ETH required from their available FETH balance.

      unchecked {
        // The if above ensures delta will not underflow.
        uint256 delta = buyPrice.price - msg.value;
        // Withdraw ETH from the buyer's account in the FETH token contract,
        // making the ETH available for `_distributeFunds` below.
        feth.marketWithdrawFrom(msg.sender, delta);
      }
    } else if (buyPrice.price < msg.value) {
      // Return any surplus funds to the buyer.

      unchecked {
        // The if above ensures this will not underflow
        payable(msg.sender).sendValue(msg.value - buyPrice.price);
      }
    }

    // Transfer the NFT to the buyer.
    // The seller was already authorized when the buyPrice was set originally set.
    _transferFromEscrow(nftContract, tokenId, msg.sender, address(0));

    // Distribute revenue for this sale.
    (uint256 protocolFee, uint256 creatorFee, uint256 sellerRev) = _distributeFunds(
      nftContract,
      tokenId,
      buyPrice.seller,
      buyPrice.price,
      referrer
    );

    emit BuyPriceAccepted(nftContract, tokenId, buyPrice.seller, msg.sender, protocolFee, creatorFee, sellerRev);
  }

  /**
   * @notice Clear a buy price and emit BuyPriceInvalidated.
   * @dev The caller must confirm the buy price is set before calling this function.
   */
  function _invalidateBuyPrice(address nftContract, uint256 tokenId) private {
    delete nftContractToTokenIdToBuyPrice[nftContract][tokenId];
    emit BuyPriceInvalidated(nftContract, tokenId);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Invalidates the buy price if one is found before transferring the NFT.
   * This will revert if there is a buy price set but the `authorizeSeller` is not the owner.
   */
  function _transferFromEscrow(
    address nftContract,
    uint256 tokenId,
    address recipient,
    address authorizeSeller
  ) internal virtual override {
    address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller;
    if (seller != address(0)) {
      // A buy price was set for this NFT.
      // `authorizeSeller != address(0) &&` could be added when other mixins use this flow.
      // ATM that additional check would never return false.
      if (seller != authorizeSeller) {
        // When there is a buy price set, the `buyPrice.seller` is the owner of the NFT.
        revert NFTMarketBuyPrice_Seller_Mismatch(seller);
      }
      // The seller authorization has been confirmed.
      authorizeSeller = address(0);

      // Invalidate the buy price as the NFT will no longer be in escrow.
      _invalidateBuyPrice(nftContract, tokenId);
    }

    super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Checks if there is a buy price set, if not then allow the transfer to proceed.
   */
  function _transferFromEscrowIfAvailable(
    address nftContract,
    uint256 tokenId,
    address recipient
  ) internal virtual override {
    address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller;
    if (seller == address(0)) {
      // A buy price has been set for this NFT so it should remain in escrow.
      super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient);
    }
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Checks if the NFT is already in escrow for buy now.
   */
  function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual override {
    address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller;
    if (seller == address(0)) {
      // The NFT is not in escrow for buy now.
      super._transferToEscrow(nftContract, tokenId);
    } else if (seller != msg.sender) {
      // When there is a buy price set, the `seller` is the owner of the NFT.
      revert NFTMarketBuyPrice_Seller_Mismatch(seller);
    }
  }

  /**
   * @notice Returns the buy price details for an NFT if one is available.
   * @dev If no price is found, seller will be address(0) and price will be max uint256.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @return seller The address of the owner that listed a buy price for this NFT.
   * Returns `address(0)` if there is no buy price set for this NFT.
   * @return price The price of the NFT.
   * Returns `0` if there is no buy price set for this NFT.
   */
  function getBuyPrice(address nftContract, uint256 tokenId) external view returns (address seller, uint256 price) {
    seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller;
    if (seller == address(0)) {
      return (seller, type(uint256).max);
    }
    price = nftContractToTokenIdToBuyPrice[nftContract][tokenId].price;
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Returns the seller if there is a buy price set for this NFT, otherwise
   * bubbles the call up for other considerations.
   */
  function _getSellerFor(address nftContract, uint256 tokenId)
    internal
    view
    virtual
    override
    returns (address payable seller)
  {
    seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller;
    if (seller == address(0)) {
      seller = super._getSellerFor(nftContract, tokenId);
    }
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[1000] private __gap;
}

File 8 of 29 : NFTMarketCore.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";

import "./Constants.sol";

import "../interfaces/IFethMarket.sol";

error NFTMarketCore_FETH_Address_Is_Not_A_Contract();
error NFTMarketCore_Only_FETH_Can_Transfer_ETH();
error NFTMarketCore_Seller_Not_Found();

/**
 * @title A place for common modifiers and functions used by various NFTMarket mixins, if any.
 * @dev This also leaves a gap which can be used to add a new mixin to the top of the inheritance tree.
 */
abstract contract NFTMarketCore is Constants {
  using AddressUpgradeable for address;

  /// @notice The FETH ERC-20 token for managing escrow and lockup.
  IFethMarket internal immutable feth;

  constructor(address _feth) {
    if (!_feth.isContract()) {
      revert NFTMarketCore_FETH_Address_Is_Not_A_Contract();
    }
    feth = IFethMarket(_feth);
  }

  /**
   * @notice Only used by FETH. Any direct transfer from users will revert.
   */
  receive() external payable {
    if (msg.sender != address(feth)) {
      revert NFTMarketCore_Only_FETH_Can_Transfer_ETH();
    }
  }

  /**
   * @notice If there is a buy price at this amount or lower, accept that and return true.
   */
  function _autoAcceptBuyPrice(
    address nftContract,
    uint256 tokenId,
    uint256 amount
  ) internal virtual returns (bool);

  /**
   * @notice If there is a valid offer at the given price or higher, accept that and return true.
   */
  function _autoAcceptOffer(
    address nftContract,
    uint256 tokenId,
    uint256 minAmount
  ) internal virtual returns (bool);

  /**
   * @notice Notify implementors when an auction has received its first bid.
   * Once a bid is received the sale is guaranteed to the auction winner
   * and other sale mechanisms become unavailable.
   * @dev Implementors of this interface should update internal state to reflect an auction has been kicked off.
   */
  function _beforeAuctionStarted(
    address, /*nftContract*/
    uint256 /*tokenId*/ // solhint-disable-next-line no-empty-blocks
  ) internal virtual {
    // No-op
  }

  /**
   * @notice Cancel the `msg.sender`'s offer if there is one, freeing up their FETH balance.
   * @dev This should be used when it does not make sense to keep the original offer around,
   * e.g. if a collector accepts a Buy Price then keeping the offer around is not necessary.
   */
  function _cancelSendersOffer(address nftContract, uint256 tokenId) internal virtual;

  /**
   * @notice Transfers the NFT from escrow and clears any state tracking this escrowed NFT.
   * @param authorizeSeller The address of the seller pending authorization.
   * Once it's been authorized by one of the escrow managers, it should be set to address(0)
   * indicated that it's no longer pending authorization.
   */
  function _transferFromEscrow(
    address nftContract,
    uint256 tokenId,
    address recipient,
    address authorizeSeller
  ) internal virtual {
    if (authorizeSeller != address(0)) {
      revert NFTMarketCore_Seller_Not_Found();
    }
    IERC721(nftContract).transferFrom(address(this), recipient, tokenId);
  }

  /**
   * @notice Transfers the NFT from escrow unless there is another reason for it to remain in escrow.
   */
  function _transferFromEscrowIfAvailable(
    address nftContract,
    uint256 tokenId,
    address recipient
  ) internal virtual {
    IERC721(nftContract).transferFrom(address(this), recipient, tokenId);
  }

  /**
   * @notice Transfers an NFT into escrow,
   * if already there this requires the msg.sender is authorized to manage the sale of this NFT.
   */
  function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual {
    IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);
  }

  /**
   * @notice Gets the FETH contract used to escrow offer funds.
   * @return fethAddress The FETH contract address.
   */
  function getFethAddress() external view returns (address fethAddress) {
    fethAddress = address(feth);
  }

  /**
   * @dev Determines the minimum amount when increasing an existing offer or bid.
   */
  function _getMinIncrement(uint256 currentAmount) internal pure returns (uint256) {
    uint256 minIncrement = currentAmount;
    unchecked {
      minIncrement /= MIN_PERCENT_INCREMENT_DENOMINATOR;
    }
    if (minIncrement == 0) {
      // Since minIncrement reduces from the currentAmount, this cannot overflow.
      // The next amount must be at least 1 wei greater than the current.
      return currentAmount + 1;
    }

    return minIncrement + currentAmount;
  }

  /**
   * @notice Checks who the seller for an NFT is, checking escrow or return the current owner if not in escrow.
   * @dev If the NFT did not have an escrowed seller to return, fall back to return the current owner.
   */
  function _getSellerFor(address nftContract, uint256 tokenId) internal view virtual returns (address payable seller) {
    seller = payable(IERC721(nftContract).ownerOf(tokenId));
  }

  /**
   * @notice Checks if an escrowed NFT is currently in active auction.
   * @return Returns false if the auction has ended, even if it has not yet been settled.
   */
  function _isInActiveAuction(address nftContract, uint256 tokenId) internal view virtual returns (bool);

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   * @dev 50 slots were consumed by adding `ReentrancyGuard`.
   */
  uint256[950] private __gap;
}

File 9 of 29 : NFTMarketCreators.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "./OZ/ERC165Checker.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "./Constants.sol";

import "../interfaces/IGetFees.sol";
import "../interfaces/IGetRoyalties.sol";
import "../interfaces/IOwnable.sol";
import "../interfaces/IRoyaltyInfo.sol";
import "../interfaces/ITokenCreator.sol";
import "@manifoldxyz/royalty-registry-solidity/contracts/IRoyaltyRegistry.sol";

error NFTMarketCreators_Address_Does_Not_Support_IRoyaltyRegistry();

/**
 * @title A mixin for associating creators to NFTs.
 * @dev In the future this may store creators directly in order to support NFTs created on a different platform.
 */
abstract contract NFTMarketCreators is
  Constants,
  ReentrancyGuardUpgradeable // Adding this unused mixin to help with linearization
{
  using OZERC165Checker for address;

  IRoyaltyRegistry private immutable royaltyRegistry;

  /**
   * @notice Configures the registry allowing for royalty overrides to be defined.
   * @param _royaltyRegistry The registry to use for royalty overrides.
   */
  constructor(address _royaltyRegistry) {
    if (!_royaltyRegistry.supportsInterface(type(IRoyaltyRegistry).interfaceId)) {
      revert NFTMarketCreators_Address_Does_Not_Support_IRoyaltyRegistry();
    }
    royaltyRegistry = IRoyaltyRegistry(_royaltyRegistry);
  }

  /**
   * @notice Looks up the royalty payment configuration for a given NFT.
   * If more than 5 royalty recipients are defined, only the first 5 are sent royalties - the others are ignored.
   * The percents distributed will always be normalized to exactly 10%, when multiple recipients are defined
   * we use the values returned to determine how much of that 10% each recipient should get.
   * @dev This will check various royalty APIs on the NFT and the royalty override
   * if one was registered with the royalty registry. This aims to send royalties
   * in the manner requested by the NFT owner, regardless of where the NFT was minted.
   */
  // solhint-disable-next-line code-complexity
  function _getCreatorPaymentInfo(address nftContract, uint256 tokenId)
    internal
    view
    returns (address payable[] memory recipients, uint256[] memory splitPerRecipientInBasisPoints)
  {
    // All NFTs implement 165 so we skip that check, individual interfaces should return false if 165 is not implemented

    // 1st priority: ERC-2981
    if (nftContract.supportsERC165Interface(type(IRoyaltyInfo).interfaceId)) {
      try IRoyaltyInfo(nftContract).royaltyInfo{ gas: READ_ONLY_GAS_LIMIT }(tokenId, BASIS_POINTS) returns (
        address receiver,
        uint256 royaltyAmount
      ) {
        // Manifold contracts return (address(this), 0) when royalties are not defined
        // - so ignore results when the amount is 0
        if (royaltyAmount > 0) {
          recipients = new address payable[](1);
          recipients[0] = payable(receiver);
          // splitPerRecipientInBasisPoints is not relevant when only 1 recipient is defined
          return (recipients, splitPerRecipientInBasisPoints);
        }
      } catch // solhint-disable-next-line no-empty-blocks
      {
        // Fall through
      }
    }

    // 2nd priority: getRoyalties
    if (nftContract.supportsERC165Interface(type(IGetRoyalties).interfaceId)) {
      try IGetRoyalties(nftContract).getRoyalties{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns (
        address payable[] memory _recipients,
        uint256[] memory recipientBasisPoints
      ) {
        uint256 recipientLen = _recipients.length;
        if (recipientLen != 0 && recipientLen == recipientBasisPoints.length) {
          return (_recipients, recipientBasisPoints);
        }
      } catch // solhint-disable-next-line no-empty-blocks
      {
        // Fall through
      }
    }

    /* Overrides must support ERC-165 when registered, except for overrides defined by the registry owner.
       If that results in an override w/o 165 we may need to upgrade the market to support or ignore that override. */
    // The registry requires overrides are not 0 and contracts when set.
    // If no override is set, the nftContract address is returned.

    try royaltyRegistry.getRoyaltyLookupAddress{ gas: READ_ONLY_GAS_LIMIT }(nftContract) returns (
      address overrideContract
    ) {
      if (overrideContract != nftContract) {
        nftContract = overrideContract;

        // The functions above are repeated here if an override is set.

        // 3rd priority: ERC-2981 override
        if (nftContract.supportsERC165Interface(type(IRoyaltyInfo).interfaceId)) {
          try IRoyaltyInfo(nftContract).royaltyInfo{ gas: READ_ONLY_GAS_LIMIT }(tokenId, BASIS_POINTS) returns (
            address receiver,
            uint256 /* royaltyAmount */
          ) {
            recipients = new address payable[](1);
            recipients[0] = payable(receiver);
            // splitPerRecipientInBasisPoints is not relevant when only 1 recipient is defined
            return (recipients, splitPerRecipientInBasisPoints);
          } catch // solhint-disable-next-line no-empty-blocks
          {
            // Fall through
          }
        }

        // 4th priority: getRoyalties override
        if (recipients.length == 0 && nftContract.supportsERC165Interface(type(IGetRoyalties).interfaceId)) {
          try IGetRoyalties(nftContract).getRoyalties{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns (
            address payable[] memory _recipients,
            uint256[] memory recipientBasisPoints
          ) {
            uint256 recipientLen = _recipients.length;
            if (recipientLen != 0 && recipientLen == recipientBasisPoints.length) {
              return (_recipients, recipientBasisPoints);
            }
          } catch // solhint-disable-next-line no-empty-blocks
          {
            // Fall through
          }
        }
      }
    } catch // solhint-disable-next-line no-empty-blocks
    {
      // Ignore out of gas errors and continue using the nftContract address
    }

    // 5th priority: getFee* from contract or override
    if (nftContract.supportsERC165Interface(type(IGetFees).interfaceId)) {
      try IGetFees(nftContract).getFeeRecipients{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns (
        address payable[] memory _recipients
      ) {
        uint256 recipientLen = _recipients.length;
        if (recipientLen != 0) {
          try IGetFees(nftContract).getFeeBps{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns (
            uint256[] memory recipientBasisPoints
          ) {
            if (recipientLen == recipientBasisPoints.length) {
              return (_recipients, recipientBasisPoints);
            }
          } catch // solhint-disable-next-line no-empty-blocks
          {
            // Fall through
          }
        }
      } catch // solhint-disable-next-line no-empty-blocks
      {
        // Fall through
      }
    }

    // 6th priority: tokenCreator w/ or w/o requiring 165 from contract or override
    try ITokenCreator(nftContract).tokenCreator{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns (
      address payable _creator
    ) {
      // Only pay the tokenCreator if there wasn't another royalty defined
      recipients = new address payable[](1);
      recipients[0] = _creator;
      // splitPerRecipientInBasisPoints is not relevant when only 1 recipient is defined
      return (recipients, splitPerRecipientInBasisPoints);
    } catch // solhint-disable-next-line no-empty-blocks
    {
      // Fall through
    }

    // 7th priority: owner from contract or override
    try IOwnable(nftContract).owner{ gas: READ_ONLY_GAS_LIMIT }() returns (address owner) {
      // Only pay the owner if there wasn't another royalty defined
      recipients = new address payable[](1);
      recipients[0] = payable(owner);
      // splitPerRecipientInBasisPoints is not relevant when only 1 recipient is defined
      return (recipients, splitPerRecipientInBasisPoints);
    } catch // solhint-disable-next-line no-empty-blocks
    {
      // Fall through
    }

    // If no valid payment address or creator is found, return 0 recipients
  }

  /**
   * @notice Returns the address of the registry allowing for royalty configuration overrides.
   * @return registry The address of the royalty registry contract.
   */
  function getRoyaltyRegistry() public view returns (address registry) {
    return address(royaltyRegistry);
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   * @dev 500 slots were consumed with the addition of `SendValueWithFallbackWithdraw`.
   */
  uint256[500] private __gap;
}

File 10 of 29 : NFTMarketFees.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

import "./Constants.sol";
import "./FoundationTreasuryNode.sol";
import "./SendValueWithFallbackWithdraw.sol";

/**
 * @title A mixin to distribute funds when an NFT is sold.
 */
abstract contract NFTMarketFees is Constants, Initializable, FoundationTreasuryNode, SendValueWithFallbackWithdraw {
  /**
   * @dev Removing old unused variables in an upgrade safe way. Was:
   * uint256 private _primaryFoundationFeeBasisPoints;
   * uint256 private _secondaryFoundationFeeBasisPoints;
   * uint256 private _secondaryCreatorFeeBasisPoints;
   * mapping(address => mapping(uint256 => bool)) private _nftContractToTokenIdToFirstSaleCompleted;
   */
  uint256[4] private __gap_was_fees;

  /// @notice The royalties sent to creator recipients on secondary sales.
  uint256 private constant CREATOR_ROYALTY_DENOMINATOR = BASIS_POINTS / 1000; // 10%
  /// @notice The fee collected by Foundation for sales facilitated by this market contract.
  uint256 private constant PROTOCOL_FEE_DENOMINATOR = BASIS_POINTS / 500; // 5%
  /// @notice The fee collected by the buy referrer for sales facilitated by this market contract.
  ///         This fee is calculated from the total protocol fee.
  /// @dev 20% of protocol fee == 1% of total sale.
  uint256 private constant BUY_REFERRER_PROTOCOL_FEE_DENOMINATOR = 5;

  /**
   * @notice Emitted when a NFT sold with a referrer.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param buyReferrer The account which received the buy referral incentive.
   * @param buyReferrerProtocolFee The portion of the protocol fee collected by the buy referrer.
   * @param buyReferrerSellerFee The portion of the owner revenue collected by the buy referrer (not implemented).
   */
  event BuyReferralPaid(
    address indexed nftContract,
    uint256 indexed tokenId,
    address buyReferrer,
    uint256 buyReferrerProtocolFee,
    uint256 buyReferrerSellerFee
  );

  /**
   * @notice Distributes funds to foundation, creator recipients, and NFT owner after a sale.
   */
  // solhint-disable-next-line code-complexity
  function _distributeFunds(
    address nftContract,
    uint256 tokenId,
    address payable seller,
    uint256 price,
    address payable buyReferrer
  )
    internal
    returns (
      uint256 protocolFee,
      uint256 creatorFee,
      uint256 sellerRev
    )
  {
    address payable[] memory creatorRecipients;
    uint256[] memory creatorShares;

    address payable sellerRevTo;
    uint256 buyReferrerProtocolFee;
    uint256 foundationProtocolFee;
    (
      foundationProtocolFee,
      creatorRecipients,
      creatorShares,
      creatorFee,
      sellerRevTo,
      sellerRev,
      buyReferrerProtocolFee
    ) = _getFees(nftContract, tokenId, seller, price, buyReferrer);

    // Keep the full fee total in protocolFee for backwards compat with events so that sum of params == sale amount.
    unchecked {
      protocolFee = foundationProtocolFee + buyReferrerProtocolFee;
    }

    if (buyReferrerProtocolFee != 0) {
      // Use standard `send` to cap the gas and prevent consuming all available
      // gas to block a tx from completing successfully.
      // Fallsback to sending the referral fee to FND on failure.
      if (_trySendValue(buyReferrer, buyReferrerProtocolFee, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT)) {
        emit BuyReferralPaid(nftContract, tokenId, buyReferrer, buyReferrerProtocolFee, 0);
      } else {
        // If we are unable to pay the referrer than the money is returned to the original protocolFee.
        unchecked {
          foundationProtocolFee += buyReferrerProtocolFee;
          buyReferrerProtocolFee = 0;
        }
      }
    }

    _sendValueWithFallbackWithdraw(
      getFoundationTreasury(),
      foundationProtocolFee,
      SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT
    );

    if (creatorFee != 0) {
      uint256 creatorRecipientsLength = creatorRecipients.length;
      if (creatorRecipientsLength > 1) {
        if (creatorRecipientsLength > MAX_ROYALTY_RECIPIENTS) {
          creatorRecipientsLength = MAX_ROYALTY_RECIPIENTS;
        }

        // Determine the total shares defined so it can be leveraged to distribute below
        uint256 totalShares;
        unchecked {
          // The array length cannot overflow 256 bits.
          for (uint256 i = 0; i < creatorRecipientsLength; ++i) {
            if (creatorShares[i] > BASIS_POINTS) {
              // If the numbers are >100% we ignore the fee recipients and pay just the first instead
              creatorRecipientsLength = 1;
              break;
            }
            // The check above ensures totalShares wont overflow.
            totalShares += creatorShares[i];
          }
        }
        if (totalShares == 0) {
          creatorRecipientsLength = 1;
        }

        // Send payouts to each additional recipient if more than 1 was defined
        uint256 totalRoyaltiesDistributed;
        for (uint256 i = 1; i < creatorRecipientsLength; ) {
          uint256 royalty = (creatorFee * creatorShares[i]) / totalShares;
          totalRoyaltiesDistributed += royalty;
          _sendValueWithFallbackWithdraw(creatorRecipients[i], royalty, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS);
          unchecked {
            ++i;
          }
        }

        // Send the remainder to the 1st creator, rounding in their favor
        _sendValueWithFallbackWithdraw(
          creatorRecipients[0],
          creatorFee - totalRoyaltiesDistributed,
          SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS
        );
      } else {
        _sendValueWithFallbackWithdraw(creatorRecipients[0], creatorFee, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS);
      }
    }
    _sendValueWithFallbackWithdraw(sellerRevTo, sellerRev, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT);
  }

  /**
   * @notice Returns how funds will be distributed for a sale at the given price point.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param price The sale price to calculate the fees for.
   * @return protocolFee How much will be sent to the Foundation treasury.
   * @return creatorRev How much will be sent across all the `creatorRecipients` defined.
   * @return creatorRecipients The addresses of the recipients to receive a portion of the creator fee.
   * @return creatorShares The percentage of the creator fee to be distributed to each `creatorRecipient`.
   * If there is only one `creatorRecipient`, this may be an empty array.
   * Otherwise `creatorShares.length` == `creatorRecipients.length`.
   * @return sellerRev How much will be sent to the owner/seller of the NFT.
   * If the NFT is being sold by the creator, this may be 0 and the full revenue will appear as `creatorRev`.
   * @return owner The address of the owner of the NFT.
   * If `sellerRev` is 0, this may be `address(0)`.
   */
  function getFeesAndRecipients(
    address nftContract,
    uint256 tokenId,
    uint256 price
  )
    external
    view
    returns (
      uint256 protocolFee,
      uint256 creatorRev,
      address payable[] memory creatorRecipients,
      uint256[] memory creatorShares,
      uint256 sellerRev,
      address payable owner
    )
  {
    address payable seller = _getSellerFor(nftContract, tokenId);
    // foundationProtocolFee == the full protocolFee since no referrers are defined here.
    (protocolFee, creatorRecipients, creatorShares, creatorRev, owner, sellerRev, ) = _getFees(
      nftContract,
      tokenId,
      seller,
      price,
      address(0)
    );
  }

  /**
   * @notice Calculates how funds should be distributed for the given sale details.
   * @dev When the NFT is being sold by the `tokenCreator`, all the seller revenue will
   * be split with the royalty recipients defined for that NFT.
   */
  function _getFees(
    address nftContract,
    uint256 tokenId,
    address payable seller,
    uint256 price,
    address buyReferrer
  )
    private
    view
    returns (
      uint256 foundationProtocolFee,
      address payable[] memory creatorRecipients,
      uint256[] memory creatorShares,
      uint256 creatorRev,
      address payable sellerRevTo,
      uint256 sellerRev,
      uint256 buyReferrerProtocolFee
    )
  {
    address creator;
    // lookup for tokenCreator
    try ITokenCreator(nftContract).tokenCreator{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns (
      address payable _creator
    ) {
      creator = _creator;
    } catch // solhint-disable-next-line no-empty-blocks
    {
      // Fall through
    }

    (creatorRecipients, creatorShares) = _getCreatorPaymentInfo(nftContract, tokenId);

    // Calculate the protocol fee
    uint256 protocolFee;
    unchecked {
      // SafeMath is not required when dividing by a non-zero constant.
      protocolFee = price / PROTOCOL_FEE_DENOMINATOR;
    }

    if (creatorRecipients.length != 0) {
      if (seller == creator || (creatorRecipients.length == 1 && seller == creatorRecipients[0])) {
        // When sold by the creator, all revenue is split if applicable.
        unchecked {
          // protocolFee is always < price.
          creatorRev = price - protocolFee;
        }
      } else {
        // Rounding favors the owner first, then creator, and foundation last.
        unchecked {
          // SafeMath is not required when dividing by a non-zero constant.
          creatorRev = price / CREATOR_ROYALTY_DENOMINATOR;
        }
        sellerRevTo = seller;
        sellerRev = price - protocolFee - creatorRev;
      }
    } else {
      // No royalty recipients found.
      sellerRevTo = seller;
      unchecked {
        // protocolFee is always < price.
        sellerRev = price - protocolFee;
      }
    }

    // Calculate the buy referrer fee if defined and not a party that already has a vested interest in this sale.
    // This is done after the sellerRev calculations as a simplification (using the full protocol fee above).
    if (buyReferrer != address(0) && buyReferrer != msg.sender && buyReferrer != seller && buyReferrer != creator) {
      // SafeMath is not required since the referrer fee is less than the total protocol fee calculated above.
      unchecked {
        buyReferrerProtocolFee = protocolFee / BUY_REFERRER_PROTOCOL_FEE_DENOMINATOR;
      }
    }

    // Calculate the foundation fee using the total protocol fee minus any referrals paid.
    unchecked {
      foundationProtocolFee = protocolFee - buyReferrerProtocolFee;
    }
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[1000] private __gap;
}

File 11 of 29 : NFTMarketOffer.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "./FoundationTreasuryNode.sol";
import "./NFTMarketCore.sol";
import "./NFTMarketFees.sol";

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

error NFTMarketOffer_Cannot_Be_Made_While_In_Auction();
/// @param currentOfferAmount The current highest offer available for this NFT.
error NFTMarketOffer_Offer_Below_Min_Amount(uint256 currentOfferAmount);
/// @param expiry The time at which the offer had expired.
error NFTMarketOffer_Offer_Expired(uint256 expiry);
/// @param currentOfferFrom The address of the collector which has made the current highest offer.
error NFTMarketOffer_Offer_From_Does_Not_Match(address currentOfferFrom);
/// @param minOfferAmount The minimum amount that must be offered in order for it to be accepted.
error NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount(uint256 minOfferAmount);
error NFTMarketOffer_Reason_Required();
error NFTMarketOffer_Provided_Contract_And_TokenId_Count_Must_Match();

/**
 * @title Allows collectors to make an offer for an NFT, valid for 24-25 hours.
 * @notice Funds are escrowed in the FETH ERC-20 token contract.
 */
abstract contract NFTMarketOffer is FoundationTreasuryNode, NFTMarketCore, ReentrancyGuardUpgradeable, NFTMarketFees {
  using AddressUpgradeable for address;

  /// @notice Stores offer details for a specific NFT.
  struct Offer {
    // Slot 1: When increasing an offer, only this slot is updated.
    /// @notice The expiration timestamp of when this offer expires.
    uint32 expiration;
    /// @notice The amount, in wei, of the highest offer.
    uint96 amount;
    /// @notice First slot (of 16B) used for the offerReferrerAddress.
    // The offerReferrerAddress is the address used to pay the
    // referrer on an accepted offer.
    uint128 offerReferrerAddressSlot0;
    // Slot 2: When the buyer changes, both slots need updating
    /// @notice The address of the collector who made this offer.
    address buyer;
    /// @notice Second slot (of 4B) used for the offerReferrerAddress.
    uint32 offerReferrerAddressSlot1;
    // 96 bits (12B) are available in slot 1.
  }

  /// @notice Stores the highest offer for each NFT.
  mapping(address => mapping(uint256 => Offer)) private nftContractToIdToOffer;

  /**
   * @notice Emitted when an offer is accepted,
   * indicating that the NFT has been transferred and revenue from the sale distributed.
   * @dev The accepted total offer amount is `protocolFee` + `creatorFee` + `sellerRev`.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param buyer The address of the collector that made the offer which was accepted.
   * @param seller The address of the seller which accepted the offer.
   * @param protocolFee The amount of ETH that was sent to Foundation for this sale.
   * @param creatorFee The amount of ETH that was sent to the creator for this sale.
   * @param sellerRev The amount of ETH that was sent to the owner for this sale.
   */
  event OfferAccepted(
    address indexed nftContract,
    uint256 indexed tokenId,
    address indexed buyer,
    address seller,
    uint256 protocolFee,
    uint256 creatorFee,
    uint256 sellerRev
  );
  /**
   * @notice Emitted when an offer is canceled by a Foundation admin.
   * @dev This should only be used for extreme cases such as DMCA takedown requests.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param reason The reason for the cancellation (a required field).
   */
  event OfferCanceledByAdmin(address indexed nftContract, uint256 indexed tokenId, string reason);
  /**
   * @notice Emitted when an offer is invalidated due to other market activity.
   * When this occurs, the collector which made the offer has their FETH balance unlocked
   * and the funds are available to place other offers or to be withdrawn.
   * @dev This occurs when the offer is no longer eligible to be accepted,
   * e.g. when a bid is placed in an auction for this NFT.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   */
  event OfferInvalidated(address indexed nftContract, uint256 indexed tokenId);
  /**
   * @notice Emitted when an offer is made.
   * @dev The `amount` of the offer is locked in the FETH ERC-20 contract, guaranteeing that the funds
   * remain available until the `expiration` date.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param buyer The address of the collector that made the offer to buy this NFT.
   * @param amount The amount, in wei, of the offer.
   * @param expiration The expiration timestamp for the offer.
   */
  event OfferMade(
    address indexed nftContract,
    uint256 indexed tokenId,
    address indexed buyer,
    uint256 amount,
    uint256 expiration
  );

  /**
   * @notice Accept the highest offer for an NFT.
   * @dev The offer must not be expired and the NFT owned + approved by the seller or
   * available in the market contract's escrow.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param offerFrom The address of the collector that you wish to sell to.
   * If the current highest offer is not from this user, the transaction will revert.
   * This could happen if a last minute offer was made by another collector,
   * and would require the seller to try accepting again.
   * @param minAmount The minimum value of the highest offer for it to be accepted.
   * If the value is less than this amount, the transaction will revert.
   * This could happen if the original offer expires and is replaced with a smaller offer.
   */
  function acceptOffer(
    address nftContract,
    uint256 tokenId,
    address offerFrom,
    uint256 minAmount
  ) external nonReentrant {
    Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];
    // Validate offer expiry and amount
    if (offer.expiration < block.timestamp) {
      revert NFTMarketOffer_Offer_Expired(offer.expiration);
    } else if (offer.amount < minAmount) {
      revert NFTMarketOffer_Offer_Below_Min_Amount(offer.amount);
    }
    // Validate the buyer
    if (offer.buyer != offerFrom) {
      revert NFTMarketOffer_Offer_From_Does_Not_Match(offer.buyer);
    }

    _acceptOffer(nftContract, tokenId);
  }

  /**
   * @notice Allows Foundation to cancel offers.
   * This will unlock the funds in the FETH ERC-20 contract for the highest offer
   * and prevent the offer from being accepted.
   * @dev This should only be used for extreme cases such as DMCA takedown requests.
   * @param nftContracts The addresses of the NFT contracts to cancel. This must be the same length as `tokenIds`.
   * @param tokenIds The ids of the NFTs to cancel. This must be the same length as `nftContracts`.
   * @param reason The reason for the cancellation (a required field).
   */
  function adminCancelOffers(
    address[] calldata nftContracts,
    uint256[] calldata tokenIds,
    string calldata reason
  ) external onlyFoundationAdmin nonReentrant {
    if (bytes(reason).length == 0) {
      revert NFTMarketOffer_Reason_Required();
    }
    if (nftContracts.length != tokenIds.length) {
      revert NFTMarketOffer_Provided_Contract_And_TokenId_Count_Must_Match();
    }

    // The array length cannot overflow 256 bits
    unchecked {
      for (uint256 i = 0; i < nftContracts.length; ++i) {
        Offer memory offer = nftContractToIdToOffer[nftContracts[i]][tokenIds[i]];
        delete nftContractToIdToOffer[nftContracts[i]][tokenIds[i]];

        if (offer.expiration >= block.timestamp) {
          // Unlock from escrow and emit an event only if the offer is still active
          feth.marketUnlockFor(offer.buyer, offer.expiration, offer.amount);
          emit OfferCanceledByAdmin(nftContracts[i], tokenIds[i], reason);
        }
        // Else continue on so the rest of the batch transaction can process successfully
      }
    }
  }

  /**
   * @notice Make an offer for any NFT which is valid for 24-25 hours.
   * The funds will be locked in the FETH token contract and become available once the offer is outbid or has expired.
   * @dev An offer may be made for an NFT before it is minted, although we generally not recommend you do that.
   * If there is a buy price set at this price or lower, that will be accepted instead of making an offer.
   * `msg.value` must be <= `amount` and any delta will be taken from the account's available FETH balance.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param amount The amount to offer for this NFT.
   * @param referrer The refrerrer address for the offer.
   * @return expiration The timestamp for when this offer will expire.
   * This is provided as a return value in case another contract would like to leverage this information,
   * user's should refer to the expiration in the `OfferMade` event log.
   * If the buy price is accepted instead, `0` is returned as the expiration since that's n/a.
   */
  function makeOfferV2(
    address nftContract,
    uint256 tokenId,
    uint256 amount,
    address payable referrer
  ) public payable returns (uint256 expiration) {
    // If there is a buy price set at this price or lower, accept that instead.
    if (_autoAcceptBuyPrice(nftContract, tokenId, amount)) {
      // If the buy price is accepted, `0` is returned as the expiration since that's n/a.
      return 0;
    }

    if (_isInActiveAuction(nftContract, tokenId)) {
      revert NFTMarketOffer_Cannot_Be_Made_While_In_Auction();
    }

    Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];

    if (offer.expiration < block.timestamp) {
      // This is a new offer for the NFT (no other offer found or the previous offer expired)

      // Lock the offer amount in FETH until the offer expires in 24-25 hours.
      expiration = feth.marketLockupFor{ value: msg.value }(msg.sender, amount);
    } else {
      // A previous offer exists and has not expired

      uint256 minIncrement = _getMinIncrement(offer.amount);
      if (amount < minIncrement) {
        // A non-trivial increase in price is required to avoid sniping
        revert NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount(minIncrement);
      }

      // Unlock the previous offer so that the FETH tokens are available for other offers or to transfer / withdraw
      // and lock the new offer amount in FETH until the offer expires in 24-25 hours.
      expiration = feth.marketChangeLockup{ value: msg.value }(
        offer.buyer,
        offer.expiration,
        offer.amount,
        msg.sender,
        amount
      );
    }

    // Record offer details
    offer.buyer = msg.sender;
    // The FETH contract guarantees that the expiration fits into 32 bits.
    offer.expiration = uint32(expiration);
    // `amount` is capped by the ETH provided, which cannot realistically overflow 96 bits.
    offer.amount = uint96(amount);
    // Set offerReferrerAddressSlot0 to the first 16B of the referrer address.
    // By shifting the referrer 32 bits to the right we obtain the first 16B.
    offer.offerReferrerAddressSlot0 = uint128(uint160(address(referrer)) >> 32);
    // Set offerReferrerAddressSlot1 to the last 4B of the referrer address.
    // By casting the referrer address to 32bits we discard the first 16B.
    offer.offerReferrerAddressSlot1 = uint32(uint160(address(referrer)));

    emit OfferMade(nftContract, tokenId, msg.sender, amount, expiration);
  }

  /**
   * @notice [DEPRECATED] Please use `makeOfferV2`.
   */
  function makeOffer(
    address nftContract,
    uint256 tokenId,
    uint256 amount
  ) external payable returns (uint256 expiration) {
    expiration = makeOfferV2(nftContract, tokenId, amount, payable(0));
  }

  /**
   * @notice Accept the highest offer for an NFT from the `msg.sender` account.
   * The NFT will be transferred to the buyer and revenue from the sale will be distributed.
   * @dev The caller must validate the expiry and amount before calling this helper.
   * This may invalidate other market tools, such as clearing the buy price if set.
   */
  function _acceptOffer(address nftContract, uint256 tokenId) private {
    Offer memory offer = nftContractToIdToOffer[nftContract][tokenId];

    // Remove offer
    delete nftContractToIdToOffer[nftContract][tokenId];
    // Withdraw ETH from the buyer's account in the FETH token contract.
    feth.marketWithdrawLocked(offer.buyer, offer.expiration, offer.amount);

    // Transfer the NFT to the buyer.
    try
      IERC721(nftContract).transferFrom(msg.sender, offer.buyer, tokenId) // solhint-disable-next-line no-empty-blocks
    {
      // NFT was in the seller's wallet so the transfer is complete.
    } catch {
      // If the transfer fails then attempt to transfer from escrow instead.
      // This should revert if `msg.sender` is not the owner of this NFT.
      _transferFromEscrow(nftContract, tokenId, offer.buyer, msg.sender);
    }

    // Distribute revenue for this sale leveraging the ETH received from the FETH contract in the line above.
    (uint256 protocolFee, uint256 creatorFee, uint256 sellerRev) = _distributeFunds(
      nftContract,
      tokenId,
      payable(msg.sender),
      offer.amount,
      _getOfferReferrerFromSlots(offer.offerReferrerAddressSlot0, offer.offerReferrerAddressSlot1)
    );

    emit OfferAccepted(nftContract, tokenId, offer.buyer, msg.sender, protocolFee, creatorFee, sellerRev);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Invalidates the highest offer when an auction is kicked off, if one is found.
   */
  function _beforeAuctionStarted(address nftContract, uint256 tokenId) internal virtual override {
    _invalidateOffer(nftContract, tokenId);
    super._beforeAuctionStarted(nftContract, tokenId);
  }

  /**
   * @inheritdoc NFTMarketCore
   */
  function _autoAcceptOffer(
    address nftContract,
    uint256 tokenId,
    uint256 minAmount
  ) internal override returns (bool) {
    Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];
    if (offer.expiration < block.timestamp || offer.amount < minAmount) {
      // No offer found, the most recent offer is now expired, or the highest offer is below the minimum amount.
      return false;
    }

    _acceptOffer(nftContract, tokenId);
    return true;
  }

  /**
   * @inheritdoc NFTMarketCore
   */
  function _cancelSendersOffer(address nftContract, uint256 tokenId) internal override {
    Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];
    if (offer.buyer == msg.sender) {
      _invalidateOffer(nftContract, tokenId);
    }
  }

  /**
   * @notice Invalidates the offer and frees ETH from escrow, if the offer has not already expired.
   * @dev Offers are not invalidated when the NFT is purchased by accepting the buy price unless it
   * was purchased by the same user.
   * The user which just purchased the NFT may have buyer's remorse and promptly decide they want a fast exit,
   * accepting a small loss to limit their exposure.
   */
  function _invalidateOffer(address nftContract, uint256 tokenId) private {
    if (nftContractToIdToOffer[nftContract][tokenId].expiration >= block.timestamp) {
      // An offer was found and it has not already expired
      Offer memory offer = nftContractToIdToOffer[nftContract][tokenId];

      // Remove offer
      delete nftContractToIdToOffer[nftContract][tokenId];

      // Unlock the offer so that the FETH tokens are available for other offers or to transfer / withdraw
      feth.marketUnlockFor(offer.buyer, offer.expiration, offer.amount);

      emit OfferInvalidated(nftContract, tokenId);
    }
  }

  /**
   * @notice Returns the minimum amount a collector must offer for this NFT in order for the offer to be valid.
   * @dev Offers for this NFT which are less than this value will revert.
   * Once the previous offer has expired smaller offers can be made.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @return minimum The minimum amount that must be offered for this NFT.
   */
  function getMinOfferAmount(address nftContract, uint256 tokenId) external view returns (uint256 minimum) {
    Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];
    if (offer.expiration >= block.timestamp) {
      return _getMinIncrement(offer.amount);
    }
    // Absolute min is anything > 0
    return 1;
  }

  /**
   * @notice Returns details about the current highest offer for an NFT.
   * @dev Default values are returned if there is no offer or the offer has expired.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @return buyer The address of the buyer that made the current highest offer.
   * Returns `address(0)` if there is no offer or the most recent offer has expired.
   * @return expiration The timestamp that the current highest offer expires.
   * Returns `0` if there is no offer or the most recent offer has expired.
   * @return amount The amount being offered for this NFT.
   * Returns `0` if there is no offer or the most recent offer has expired.
   */
  function getOffer(address nftContract, uint256 tokenId)
    external
    view
    returns (
      address buyer,
      uint256 expiration,
      uint256 amount
    )
  {
    Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];
    if (offer.expiration < block.timestamp) {
      // Offer not found or has expired
      return (address(0), 0, 0);
    }

    // An offer was found and it has not yet expired.
    return (offer.buyer, offer.expiration, offer.amount);
  }

  /**
   * @notice Returns the current highest offer's referral for an NFT.
   * @dev Default value of `payable(0)` is returned if
   * there is no offer, the offer has expired or does not have a referral.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @return referrer The payable address of the referrer for the offer.
   */
  function getOfferReferrer(address nftContract, uint256 tokenId) external view returns (address payable referrer) {
    Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];
    if (offer.expiration < block.timestamp) {
      // Offer not found or has expired
      return payable(0);
    }
    return _getOfferReferrerFromSlots(offer.offerReferrerAddressSlot0, offer.offerReferrerAddressSlot1);
  }

  function _getOfferReferrerFromSlots(uint128 offerReferrerAddressSlot0, uint32 offerReferrerAddressSlot1)
    private
    pure
    returns (address payable referrer)
  {
    // Stitch offerReferrerAddressSlot0 and offerReferrerAddressSlot1 to obtain the payable offerReferrerAddress.
    // Left shift offerReferrerAddressSlot0 by 32 bits OR it with offerReferrerAddressSlot1.
    referrer = payable(address((uint160(offerReferrerAddressSlot0) << 32) | uint160(offerReferrerAddressSlot1)));
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[1000] private __gap;
}

File 12 of 29 : NFTMarketPrivateSale.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

import "./NFTMarketFees.sol";

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

error NFTMarketPrivateSale_Can_Be_Offered_For_24Hrs_Max();
error NFTMarketPrivateSale_Signature_Canceled_Or_Already_Claimed();
error NFTMarketPrivateSale_Proxy_Address_Is_Not_A_Contract();
error NFTMarketPrivateSale_Sale_Expired();
error NFTMarketPrivateSale_Signature_Verification_Failed();
error NFTMarketPrivateSale_Too_Much_Value_Provided();

/**
 * @title Allows owners to offer an NFT for sale to a specific collector.
 * @notice Private sales are authorized by the seller with an EIP-712 signature.
 * @dev Private sale offers must be accepted by the buyer before they expire, typically in 24 hours.
 */
abstract contract NFTMarketPrivateSale is NFTMarketFees {
  using AddressUpgradeable for address;
  using ECDSA for bytes32;

  /// @dev This value was replaced with an immutable version.
  bytes32 private __gap_was_DOMAIN_SEPARATOR;

  /// @notice Tracks if a private sale has already been used.
  /// @dev Maps nftContract -> tokenId -> buyer -> seller -> amount -> deadline -> invalidated.
  // solhint-disable-next-line max-line-length
  mapping(address => mapping(uint256 => mapping(address => mapping(address => mapping(uint256 => mapping(uint256 => bool))))))
    private privateSaleInvalidated;

  /// @notice The domain used in EIP-712 signatures.
  /// @dev It is not a constant so that the chainId can be determined dynamically.
  /// If multiple classes use EIP-712 signatures in the future this can move to a shared file.
  bytes32 private immutable DOMAIN_SEPARATOR;

  /// @notice The hash of the private sale method signature used for EIP-712 signatures.
  bytes32 private constant BUY_FROM_PRIVATE_SALE_TYPEHASH =
    keccak256("BuyFromPrivateSale(address nftContract,uint256 tokenId,address buyer,uint256 price,uint256 deadline)");
  /// @notice The name used in the EIP-712 domain.
  /// @dev If multiple classes use EIP-712 signatures in the future this can move to the shared constants file.
  string private constant NAME = "FNDNFTMarket";

  /**
   * @notice Emitted when an NFT is sold in a private sale.
   * @dev The total amount of this sale is `protocolFee` + `creatorFee` + `sellerRev`.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The ID of the NFT.
   * @param seller The address of the seller.
   * @param buyer The address of the buyer.
   * @param protocolFee The amount of ETH that was sent to Foundation for this sale.
   * @param creatorFee The amount of ETH that was sent to the creator for this sale.
   * @param sellerRev The amount of ETH that was sent to the owner for this sale.
   * @param deadline When the private sale offer was set to expire.
   */
  event PrivateSaleFinalized(
    address indexed nftContract,
    uint256 indexed tokenId,
    address indexed seller,
    address buyer,
    uint256 protocolFee,
    uint256 creatorFee,
    uint256 sellerRev,
    uint256 deadline
  );

  /**
   * @notice Configures the contract to accept EIP-712 signatures.
   * @param marketProxyAddress The address of the proxy contract which will be called when accepting a private sale.
   */
  constructor(address marketProxyAddress) {
    if (!marketProxyAddress.isContract()) {
      revert NFTMarketPrivateSale_Proxy_Address_Is_Not_A_Contract();
    }
    DOMAIN_SEPARATOR = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256(bytes(NAME)),
        // Incrementing the version can be used to invalidate previously signed messages.
        keccak256(bytes("1")),
        block.chainid,
        marketProxyAddress
      )
    );
  }

  /**
   * @notice Buy an NFT from a private sale.
   * @dev This API is deprecated and will be removed in the future, `buyFromPrivateSaleFor` should be used instead.
   * The seller signs a message approving the sale and then the buyer calls this function
   * with the `msg.value` equal to the agreed upon price.
   * If the seller is no longer the `ownerOf` this NFT or has removed approval for this contract,
   * attempts to purchase from private sale will revert.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The ID of the NFT.
   * @param deadline The timestamp at which the offer to sell will expire.
   * @param v The v value of the EIP-712 signature.
   * @param r The r value of the EIP-712 signature.
   * @param s The s value of the EIP-712 signature.
   */
  function buyFromPrivateSale(
    address nftContract,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external payable {
    buyFromPrivateSaleFor(nftContract, tokenId, msg.value, deadline, v, r, s);
  }

  /**
   * @notice Buy an NFT from a private sale.
   * @dev The seller signs a message approving the sale and then the buyer calls this function
   * with the `amount` equal to the agreed upon price.
   * If the seller is no longer the `ownerOf` this NFT or has removed approval for this contract,
   * attempts to purchase from private sale will revert.
   * @dev `amount` - `msg.value` is withdrawn from the bidder's FETH balance.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The ID of the NFT.
   * @param amount The amount to buy for, if this is more than `msg.value` funds will be
   * withdrawn from your FETH balance.
   * @param deadline The timestamp at which the offer to sell will expire.
   * @param v The v value of the EIP-712 signature.
   * @param r The r value of the EIP-712 signature.
   * @param s The s value of the EIP-712 signature.
   */
  function buyFromPrivateSaleFor(
    address nftContract,
    uint256 tokenId,
    uint256 amount,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public payable nonReentrant {
    // now + 2 days cannot overflow
    unchecked {
      if (deadline < block.timestamp) {
        // The signed message from the seller has expired.
        revert NFTMarketPrivateSale_Sale_Expired();
      } else if (deadline > block.timestamp + 2 days) {
        // Private sales typically expire in 24 hours, but 2 days is used here in order to ensure
        // that transactions do not fail due to a minor timezone error or similar during signing.

        // This prevents malicious actors from requesting signatures that never expire.
        revert NFTMarketPrivateSale_Can_Be_Offered_For_24Hrs_Max();
      }
    }

    // Cancel the buyer's offer if there is one in order to free up their FETH balance
    // even if they don't need the FETH for this specific purchase.
    _cancelSendersOffer(address(nftContract), tokenId);

    if (amount > msg.value) {
      // Withdraw additional ETH required from their available FETH balance.

      unchecked {
        // The if above ensures delta will not underflow
        feth.marketWithdrawFrom(msg.sender, amount - msg.value);
      }
    } else if (amount < msg.value) {
      // The terms of the sale cannot change, so if too much ETH is sent then something went wrong.
      revert NFTMarketPrivateSale_Too_Much_Value_Provided();
    }

    // The seller must have the NFT in their wallet when this function is called,
    // otherwise the signature verification below will fail.
    address payable seller = payable(IERC721(nftContract).ownerOf(tokenId));

    // Ensure that the offer can only be accepted once.
    if (privateSaleInvalidated[nftContract][tokenId][msg.sender][seller][amount][deadline]) {
      revert NFTMarketPrivateSale_Signature_Canceled_Or_Already_Claimed();
    }
    privateSaleInvalidated[nftContract][tokenId][msg.sender][seller][amount][deadline] = true;

    // Scoping this block to avoid a stack too deep error
    {
      bytes32 digest = keccak256(
        abi.encodePacked(
          "\x19\x01",
          DOMAIN_SEPARATOR,
          keccak256(abi.encode(BUY_FROM_PRIVATE_SALE_TYPEHASH, nftContract, tokenId, msg.sender, amount, deadline))
        )
      );

      // Revert if the signature is invalid, the terms are not as expected, or if the seller transferred the NFT.
      if (digest.recover(v, r, s) != seller) {
        revert NFTMarketPrivateSale_Signature_Verification_Failed();
      }
    }

    // This should revert if the seller has not given the market contract approval.
    IERC721(nftContract).transferFrom(seller, msg.sender, tokenId);

    // Distribute revenue for this sale.
    (uint256 protocolFee, uint256 creatorFee, uint256 sellerRev) = _distributeFunds(
      nftContract,
      tokenId,
      seller,
      amount,
      payable(address(0))
    );

    emit PrivateSaleFinalized(nftContract, tokenId, seller, msg.sender, protocolFee, creatorFee, sellerRev, deadline);
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   * @dev 1 slot was consumed by privateSaleInvalidated.
   */
  uint256[999] private __gap;
}

File 13 of 29 : NFTMarketReserveAuction.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "./Constants.sol";
import "./FoundationTreasuryNode.sol";
import "./NFTMarketAuction.sol";
import "./NFTMarketCore.sol";
import "./NFTMarketFees.sol";
import "./SendValueWithFallbackWithdraw.sol";

/// @param auctionId The already listed auctionId for this NFT.
error NFTMarketReserveAuction_Already_Listed(uint256 auctionId);
/// @param minAmount The minimum amount that must be bid in order for it to be accepted.
error NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount(uint256 minAmount);
error NFTMarketReserveAuction_Cannot_Admin_Cancel_Without_Reason();
/// @param reservePrice The current reserve price.
error NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price(uint256 reservePrice);
/// @param endTime The timestamp at which the auction had ended.
error NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction(uint256 endTime);
error NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction();
error NFTMarketReserveAuction_Cannot_Cancel_Nonexistent_Auction();
error NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction();
/// @param endTime The timestamp at which the auction will end.
error NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress(uint256 endTime);
error NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid();
error NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress();
/// @param maxDuration The maximum configuration for a duration of the auction, in seconds.
error NFTMarketReserveAuction_Exceeds_Max_Duration(uint256 maxDuration);
/// @param extensionDuration The extension duration, in seconds.
error NFTMarketReserveAuction_Less_Than_Extension_Duration(uint256 extensionDuration);
error NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price();
/// @param seller The current owner of the NFT.
error NFTMarketReserveAuction_Not_Matching_Seller(address seller);
/// @param owner The current owner of the NFT.
error NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(address owner);
error NFTMarketReserveAuction_Price_Already_Set();
error NFTMarketReserveAuction_Too_Much_Value_Provided();

/**
 * @title Allows the owner of an NFT to list it in auction.
 * @notice NFTs in auction are escrowed in the market contract.
 * @dev There is room to optimize the storage for auctions, significantly reducing gas costs.
 * This may be done in the future, but for now it will remain as is in order to ease upgrade compatibility.
 */
abstract contract NFTMarketReserveAuction is
  Constants,
  FoundationTreasuryNode,
  NFTMarketCore,
  ReentrancyGuardUpgradeable,
  SendValueWithFallbackWithdraw,
  NFTMarketFees,
  NFTMarketAuction
{
  /// @notice The auction configuration for a specific NFT.
  struct ReserveAuction {
    /// @notice The address of the NFT contract.
    address nftContract;
    /// @notice The id of the NFT.
    uint256 tokenId;
    /// @notice The owner of the NFT which listed it in auction.
    address payable seller;
    /// @notice The duration for this auction.
    uint256 duration;
    /// @notice The extension window for this auction.
    uint256 extensionDuration;
    /// @notice The time at which this auction will not accept any new bids.
    /// @dev This is `0` until the first bid is placed.
    uint256 endTime;
    /// @notice The current highest bidder in this auction.
    /// @dev This is `address(0)` until the first bid is placed.
    address payable bidder;
    /// @notice The latest price of the NFT in this auction.
    /// @dev This is set to the reserve price, and then to the highest bid once the auction has started.
    uint256 amount;
  }

  /// @notice Stores the auction configuration for a specific NFT.
  /// @dev This allows us to modify the storage struct without changing external APIs.
  struct ReserveAuctionStorage {
    /// @notice The address of the NFT contract.
    address nftContract;
    /// @notice The id of the NFT.
    uint256 tokenId;
    /// @notice The owner of the NFT which listed it in auction.
    address payable seller;
    /// @notice First slot (of 12B) used for the bidReferrerAddress.
    /// The bidReferrerAddress is the address used to pay the referrer on finalize.
    /// @dev This approach is used in order to pack storage, saving gas.
    uint96 bidReferrerAddressSlot0;
    /// @dev This field is no longer used.
    uint256 __gap_was_duration;
    /// @dev This field is no longer used.
    uint256 __gap_was_extensionDuration;
    /// @notice The time at which this auction will not accept any new bids.
    /// @dev This is `0` until the first bid is placed.
    uint256 endTime;
    /// @notice The current highest bidder in this auction.
    /// @dev This is `address(0)` until the first bid is placed.
    address payable bidder;
    /// @notice Second slot (of 8B) used for the bidReferrerAddress.
    uint64 bidReferrerAddressSlot1;
    /// @notice The latest price of the NFT in this auction.
    /// @dev This is set to the reserve price, and then to the highest bid once the auction has started.
    uint256 amount;
  }

  /// @notice The auction configuration for a specific auction id.
  mapping(address => mapping(uint256 => uint256)) private nftContractToTokenIdToAuctionId;
  /// @notice The auction id for a specific NFT.
  /// @dev This is deleted when an auction is finalized or canceled.
  mapping(uint256 => ReserveAuctionStorage) private auctionIdToAuction;

  /**
   * @dev Removing old unused variables in an upgrade safe way. Was:
   * uint256 private __gap_was_minPercentIncrementInBasisPoints;
   * uint256 private __gap_was_maxBidIncrementRequirement;
   * uint256 private __gap_was_duration;
   * uint256 private __gap_was_extensionDuration;
   * uint256 private __gap_was_goLiveDate;
   */
  uint256[5] private __gap_was_config;

  /// @notice How long an auction lasts for once the first bid has been received.
  uint256 private immutable DURATION;

  /// @notice The window for auction extensions, any bid placed in the final 15 minutes
  /// of an auction will reset the time remaining to 15 minutes.
  uint256 private constant EXTENSION_DURATION = 15 minutes;

  /// @notice Caps the max duration that may be configured so that overflows will not occur.
  uint256 private constant MAX_MAX_DURATION = 1000 days;

  /**
   * @notice Emitted when a bid is placed.
   * @param auctionId The id of the auction this bid was for.
   * @param bidder The address of the bidder.
   * @param amount The amount of the bid.
   * @param endTime The new end time of the auction (which may have been set or extended by this bid).
   */
  event ReserveAuctionBidPlaced(uint256 indexed auctionId, address indexed bidder, uint256 amount, uint256 endTime);
  /**
   * @notice Emitted when an auction is cancelled.
   * @dev This is only possible if the auction has not received any bids.
   * @param auctionId The id of the auction that was cancelled.
   */
  event ReserveAuctionCanceled(uint256 indexed auctionId);
  /**
   * @notice Emitted when an auction is canceled by a Foundation admin.
   * @dev When this occurs, the highest bidder (if there was a bid) is automatically refunded.
   * @param auctionId The id of the auction that was cancelled.
   * @param reason The reason for the cancellation.
   */
  event ReserveAuctionCanceledByAdmin(uint256 indexed auctionId, string reason);
  /**
   * @notice Emitted when an NFT is listed for auction.
   * @param seller The address of the seller.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param duration The duration of the auction (always 24-hours).
   * @param extensionDuration The duration of the auction extension window (always 15-minutes).
   * @param reservePrice The reserve price to kick off the auction.
   * @param auctionId The id of the auction that was created.
   */
  event ReserveAuctionCreated(
    address indexed seller,
    address indexed nftContract,
    uint256 indexed tokenId,
    uint256 duration,
    uint256 extensionDuration,
    uint256 reservePrice,
    uint256 auctionId
  );
  /**
   * @notice Emitted when an auction that has already ended is finalized,
   * indicating that the NFT has been transferred and revenue from the sale distributed.
   * @dev The amount of the highest bid / final sale price for this auction
   * is `protocolFee` + `creatorFee` + `sellerRev`.
   * @param auctionId The id of the auction that was finalized.
   * @param seller The address of the seller.
   * @param bidder The address of the highest bidder that won the NFT.
   * @param protocolFee The amount of ETH that was sent to Foundation for this sale.
   * @param creatorFee The amount of ETH that was sent to the creator for this sale.
   * @param sellerRev The amount of ETH that was sent to the owner for this sale.
   */
  event ReserveAuctionFinalized(
    uint256 indexed auctionId,
    address indexed seller,
    address indexed bidder,
    uint256 protocolFee,
    uint256 creatorFee,
    uint256 sellerRev
  );
  /**
   * @notice Emitted when an auction is invalidated due to other market activity.
   * @dev This occurs when the NFT is sold another way, such as with `buy` or `acceptOffer`.
   * @param auctionId The id of the auction that was invalidated.
   */
  event ReserveAuctionInvalidated(uint256 indexed auctionId);
  /**
   * @notice Emitted when the auction's reserve price is changed.
   * @dev This is only possible if the auction has not received any bids.
   * @param auctionId The id of the auction that was updated.
   * @param reservePrice The new reserve price for the auction.
   */
  event ReserveAuctionUpdated(uint256 indexed auctionId, uint256 reservePrice);

  /// @notice Confirms that the reserve price is not zero.
  modifier onlyValidAuctionConfig(uint256 reservePrice) {
    if (reservePrice == 0) {
      revert NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price();
    }
    _;
  }

  /**
   * @notice Configures the duration for auctions.
   * @param duration The duration for auctions, in seconds.
   */
  constructor(uint256 duration) {
    if (duration > MAX_MAX_DURATION) {
      // This ensures that math in this file will not overflow due to a huge duration.
      revert NFTMarketReserveAuction_Exceeds_Max_Duration(MAX_MAX_DURATION);
    }
    if (duration < EXTENSION_DURATION) {
      // The auction duration configuration must be greater than the extension window of 15 minutes
      revert NFTMarketReserveAuction_Less_Than_Extension_Duration(EXTENSION_DURATION);
    }
    DURATION = duration;
  }

  /**
   * @notice Allows Foundation to cancel an auction, refunding the bidder and returning the NFT to
   * the seller (if not active buy price set).
   * This should only be used for extreme cases such as DMCA takedown requests.
   * @param auctionId The id of the auction to cancel.
   * @param reason The reason for the cancellation (a required field).
   */
  function adminCancelReserveAuction(uint256 auctionId, string calldata reason)
    external
    onlyFoundationAdmin
    nonReentrant
  {
    if (bytes(reason).length == 0) {
      revert NFTMarketReserveAuction_Cannot_Admin_Cancel_Without_Reason();
    }
    ReserveAuctionStorage memory auction = auctionIdToAuction[auctionId];
    if (auction.amount == 0) {
      revert NFTMarketReserveAuction_Cannot_Cancel_Nonexistent_Auction();
    }

    delete nftContractToTokenIdToAuctionId[auction.nftContract][auction.tokenId];
    delete auctionIdToAuction[auctionId];

    // Return the NFT to the owner.
    _transferFromEscrowIfAvailable(auction.nftContract, auction.tokenId, auction.seller);

    if (auction.bidder != address(0)) {
      // Refund the highest bidder if any bids were placed in this auction.
      _sendValueWithFallbackWithdraw(auction.bidder, auction.amount, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT);
    }

    emit ReserveAuctionCanceledByAdmin(auctionId, reason);
  }

  /**
   * @notice If an auction has been created but has not yet received bids, it may be canceled by the seller.
   * @dev The NFT is transferred back to the owner unless there is still a buy price set.
   * @param auctionId The id of the auction to cancel.
   */
  function cancelReserveAuction(uint256 auctionId) external nonReentrant {
    ReserveAuctionStorage memory auction = auctionIdToAuction[auctionId];
    if (auction.seller != msg.sender) {
      revert NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(auction.seller);
    }
    if (auction.endTime != 0) {
      revert NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress();
    }

    // Remove the auction.
    delete nftContractToTokenIdToAuctionId[auction.nftContract][auction.tokenId];
    delete auctionIdToAuction[auctionId];

    // Transfer the NFT unless it still has a buy price set.
    _transferFromEscrowIfAvailable(auction.nftContract, auction.tokenId, auction.seller);

    emit ReserveAuctionCanceled(auctionId);
  }

  /**
   * @notice Creates an auction for the given NFT.
   * The NFT is held in escrow until the auction is finalized or canceled.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param reservePrice The initial reserve price for the auction.
   */
  function createReserveAuction(
    address nftContract,
    uint256 tokenId,
    uint256 reservePrice
  ) external nonReentrant onlyValidAuctionConfig(reservePrice) {
    uint256 auctionId = _getNextAndIncrementAuctionId();

    // If the `msg.sender` is not the owner of the NFT, transferring into escrow should fail.
    _transferToEscrow(nftContract, tokenId);

    // This check must be after _transferToEscrow in case auto-settle was required
    if (nftContractToTokenIdToAuctionId[nftContract][tokenId] != 0) {
      revert NFTMarketReserveAuction_Already_Listed(nftContractToTokenIdToAuctionId[nftContract][tokenId]);
    }

    // Store the auction details
    nftContractToTokenIdToAuctionId[nftContract][tokenId] = auctionId;
    ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId];
    auction.nftContract = nftContract;
    auction.tokenId = tokenId;
    auction.seller = payable(msg.sender);
    auction.amount = reservePrice;

    emit ReserveAuctionCreated(msg.sender, nftContract, tokenId, DURATION, EXTENSION_DURATION, reservePrice, auctionId);
  }

  /**
   * @notice Once the countdown has expired for an auction, anyone can settle the auction.
   * This will send the NFT to the highest bidder and distribute revenue for this sale.
   * @param auctionId The id of the auction to settle.
   */
  function finalizeReserveAuction(uint256 auctionId) external nonReentrant {
    if (auctionIdToAuction[auctionId].endTime == 0) {
      revert NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction();
    }
    _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: false });
  }

  /**
   * @notice Place a bid in an auction.
   * A bidder may place a bid which is at least the value defined by `getMinBidAmount`.
   * If this is the first bid on the auction, the countdown will begin.
   * If there is already an outstanding bid, the previous bidder will be refunded at this time
   * and if the bid is placed in the final moments of the auction, the countdown may be extended.
   * @dev This API is deprecated and will be removed in the future, `placeBidV2` should be used instead.
   * @param auctionId The id of the auction to bid on.
   */
  function placeBid(uint256 auctionId) external payable {
    placeBidV2(auctionId, msg.value, payable(0));
  }

  /**
   * @notice Place a bid in an auction.
   * A bidder may place a bid which is at least the amount defined by `getMinBidAmount`.
   * If this is the first bid on the auction, the countdown will begin.
   * If there is already an outstanding bid, the previous bidder will be refunded at this time
   * and if the bid is placed in the final moments of the auction, the countdown may be extended.
   * @dev `amount` - `msg.value` is withdrawn from the bidder's FETH balance.
   * @param auctionId The id of the auction to bid on.
   * @param amount The amount to bid, if this is more than `msg.value` funds will be withdrawn from your FETH balance.
   */
  /* solhint-disable-next-line code-complexity */
  function placeBidV2(
    uint256 auctionId,
    uint256 amount,
    address payable referrer
  ) public payable nonReentrant {
    ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId];

    if (auction.amount == 0) {
      // No auction found
      revert NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction();
    } else if (amount < msg.value) {
      // The amount is specified by the bidder, so if too much ETH is sent then something went wrong.
      revert NFTMarketReserveAuction_Too_Much_Value_Provided();
    }

    uint256 endTime = auction.endTime;

    // Store the bid referral
    if (referrer != address(0) || endTime != 0) {
      auction.bidReferrerAddressSlot0 = uint96(uint160(address(referrer)) >> 64);
      auction.bidReferrerAddressSlot1 = uint64(uint160(address(referrer)));
    }

    if (endTime == 0) {
      // This is the first bid, kicking off the auction.

      if (amount < auction.amount) {
        // The bid must be >= the reserve price.
        revert NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price(auction.amount);
      }

      // Notify other market tools that an auction for this NFT has been kicked off.
      // The only state change before this call is potentially withdrawing funds from FETH.
      _beforeAuctionStarted(auction.nftContract, auction.tokenId);

      // Store the bid details.
      auction.amount = amount;
      auction.bidder = payable(msg.sender);

      // On the first bid, set the endTime to now + duration.
      unchecked {
        // Duration is always set to 24hrs so the below can't overflow.
        endTime = block.timestamp + DURATION;
      }
      auction.endTime = endTime;
    } else {
      if (endTime < block.timestamp) {
        // The auction has already ended.
        revert NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction(endTime);
      } else if (auction.bidder == msg.sender) {
        // We currently do not allow a bidder to increase their bid unless another user has outbid them first.
        revert NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid();
      } else {
        uint256 minIncrement = _getMinIncrement(auction.amount);
        if (amount < minIncrement) {
          // If this bid outbids another, it must be at least 10% greater than the last bid.
          revert NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount(minIncrement);
        }
      }

      // Cache and update bidder state
      uint256 originalAmount = auction.amount;
      address payable originalBidder = auction.bidder;
      auction.amount = amount;
      auction.bidder = payable(msg.sender);

      unchecked {
        // When a bid outbids another, check to see if a time extension should apply.
        // We confirmed that the auction has not ended, so endTime is always >= the current timestamp.
        // Current time plus extension duration (always 15 mins) cannot overflow.
        uint256 endTimeWithExtension = block.timestamp + EXTENSION_DURATION;
        if (endTime < endTimeWithExtension) {
          endTime = endTimeWithExtension;
          auction.endTime = endTime;
        }
      }

      // Refund the previous bidder
      _sendValueWithFallbackWithdraw(originalBidder, originalAmount, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT);
    }

    // Withdraw last in order to leverage freed FETH balance.
    if (amount > msg.value) {
      // Withdraw additional ETH required from their available FETH balance.

      unchecked {
        // The if above ensures delta will not underflow.
        // Withdraw ETH from the buyer's account in the FETH token contract.
        feth.marketWithdrawFrom(msg.sender, amount - msg.value);
      }
    }

    emit ReserveAuctionBidPlaced(auctionId, msg.sender, amount, endTime);
  }

  /**
   * @notice If an auction has been created but has not yet received bids, the reservePrice may be
   * changed by the seller.
   * @param auctionId The id of the auction to change.
   * @param reservePrice The new reserve price for this auction.
   */
  function updateReserveAuction(uint256 auctionId, uint256 reservePrice) external onlyValidAuctionConfig(reservePrice) {
    ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId];
    if (auction.seller != msg.sender) {
      revert NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(auction.seller);
    } else if (auction.endTime != 0) {
      revert NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress();
    } else if (auction.amount == reservePrice) {
      revert NFTMarketReserveAuction_Price_Already_Set();
    }

    // Update the current reserve price.
    auction.amount = reservePrice;

    emit ReserveAuctionUpdated(auctionId, reservePrice);
  }

  /**
   * @notice Settle an auction that has already ended.
   * This will send the NFT to the highest bidder and distribute revenue for this sale.
   * @param keepInEscrow If true, the NFT will be kept in escrow to save gas by avoiding
   * redundant transfers if the NFT should remain in escrow, such as when the new owner
   * sets a buy price or lists it in a new auction.
   */
  function _finalizeReserveAuction(uint256 auctionId, bool keepInEscrow) private {
    ReserveAuctionStorage memory auction = auctionIdToAuction[auctionId];

    if (auction.endTime >= block.timestamp) {
      revert NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress(auction.endTime);
    }

    // Remove the auction.
    delete nftContractToTokenIdToAuctionId[auction.nftContract][auction.tokenId];
    delete auctionIdToAuction[auctionId];

    if (!keepInEscrow) {
      // The seller was authorized when the auction was originally created
      super._transferFromEscrow(auction.nftContract, auction.tokenId, auction.bidder, address(0));
    }

    // Distribute revenue for this sale.
    (uint256 protocolFee, uint256 creatorFee, uint256 sellerRev) = _distributeFunds(
      auction.nftContract,
      auction.tokenId,
      auction.seller,
      auction.amount,
      payable(address((uint160(auction.bidReferrerAddressSlot0) << 64) | uint160(auction.bidReferrerAddressSlot1)))
    );

    emit ReserveAuctionFinalized(auctionId, auction.seller, auction.bidder, protocolFee, creatorFee, sellerRev);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev If an auction is found:
   *  - If the auction is over, it will settle the auction and confirm the new seller won the auction.
   *  - If the auction has not received a bid, it will invalidate the auction.
   *  - If the auction is in progress, this will revert.
   */
  function _transferFromEscrow(
    address nftContract,
    uint256 tokenId,
    address recipient,
    address authorizeSeller
  ) internal virtual override {
    uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId];
    if (auctionId != 0) {
      ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId];
      if (auction.endTime == 0) {
        // The auction has not received any bids yet so it may be invalided.

        if (authorizeSeller != address(0) && auction.seller != authorizeSeller) {
          // The account trying to transfer the NFT is not the current owner.
          revert NFTMarketReserveAuction_Not_Matching_Seller(auction.seller);
        }

        // Remove the auction.
        delete nftContractToTokenIdToAuctionId[nftContract][tokenId];
        delete auctionIdToAuction[auctionId];

        emit ReserveAuctionInvalidated(auctionId);
      } else {
        // If the auction has ended, the highest bidder will be the new owner
        // and if the auction is in progress, this will revert.

        // `authorizeSeller != address(0)` does not apply here since an unsettled auction must go
        // through this path to know who the authorized seller should be.
        if (auction.bidder != authorizeSeller) {
          revert NFTMarketReserveAuction_Not_Matching_Seller(auction.bidder);
        }

        // Finalization will revert if the auction has not yet ended.
        _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: true });
      }
      // The seller authorization has been confirmed.
      authorizeSeller = address(0);
    }

    super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller);
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Checks if there is an auction for this NFT before allowing the transfer to continue.
   */
  function _transferFromEscrowIfAvailable(
    address nftContract,
    uint256 tokenId,
    address recipient
  ) internal virtual override {
    if (nftContractToTokenIdToAuctionId[nftContract][tokenId] == 0) {
      // No auction was found

      super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient);
    }
  }

  /**
   * @inheritdoc NFTMarketCore
   */
  function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual override {
    uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId];
    if (auctionId == 0) {
      // NFT is not in auction
      super._transferToEscrow(nftContract, tokenId);
      return;
    }
    // Using storage saves gas since most of the data is not needed
    ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId];
    if (auction.endTime == 0) {
      // Reserve price set, confirm the seller is a match
      if (auction.seller != msg.sender) {
        revert NFTMarketReserveAuction_Not_Matching_Seller(auction.seller);
      }
    } else {
      // Auction in progress, confirm the highest bidder is a match
      if (auction.bidder != msg.sender) {
        revert NFTMarketReserveAuction_Not_Matching_Seller(auction.bidder);
      }

      // Finalize auction but leave NFT in escrow, reverts if the auction has not ended
      _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: true });
    }
  }

  /**
   * @notice Returns the minimum amount a bidder must spend to participate in an auction.
   * Bids must be greater than or equal to this value or they will revert.
   * @param auctionId The id of the auction to check.
   * @return minimum The minimum amount for a bid to be accepted.
   */
  function getMinBidAmount(uint256 auctionId) external view returns (uint256 minimum) {
    ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId];
    if (auction.endTime == 0) {
      return auction.amount;
    }
    return _getMinIncrement(auction.amount);
  }

  /**
   * @notice Returns auction details for a given auctionId.
   * @param auctionId The id of the auction to lookup.
   */
  function getReserveAuction(uint256 auctionId) external view returns (ReserveAuction memory auction) {
    ReserveAuctionStorage storage auctionStorage = auctionIdToAuction[auctionId];
    auction = ReserveAuction(
      auctionStorage.nftContract,
      auctionStorage.tokenId,
      auctionStorage.seller,
      DURATION,
      EXTENSION_DURATION,
      auctionStorage.endTime,
      auctionStorage.bidder,
      auctionStorage.amount
    );
  }

  /**
   * @notice Returns the auctionId for a given NFT, or 0 if no auction is found.
   * @dev If an auction is canceled, it will not be returned. However the auction may be over and pending finalization.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @return auctionId The id of the auction, or 0 if no auction is found.
   */
  function getReserveAuctionIdFor(address nftContract, uint256 tokenId) external view returns (uint256 auctionId) {
    auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId];
  }

  /**
   * @notice Returns the referrer for the current highest bid in the auction, or address(0).
   */
  function getReserveAuctionBidReferrer(uint256 auctionId) external view returns (address payable referrer) {
    ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId];
    referrer = payable(
      address((uint160(auction.bidReferrerAddressSlot0) << 64) | uint160(auction.bidReferrerAddressSlot1))
    );
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Returns the seller that has the given NFT in escrow for an auction,
   * or bubbles the call up for other considerations.
   */
  function _getSellerFor(address nftContract, uint256 tokenId)
    internal
    view
    virtual
    override
    returns (address payable seller)
  {
    seller = auctionIdToAuction[nftContractToTokenIdToAuctionId[nftContract][tokenId]].seller;
    if (seller == address(0)) {
      seller = super._getSellerFor(nftContract, tokenId);
    }
  }

  /**
   * @inheritdoc NFTMarketCore
   */
  function _isInActiveAuction(address nftContract, uint256 tokenId) internal view override returns (bool) {
    uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId];
    return auctionId != 0 && auctionIdToAuction[auctionId].endTime >= block.timestamp;
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[1000] private __gap;
}

File 14 of 29 : SendValueWithFallbackWithdraw.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "./FoundationTreasuryNode.sol";
import "./NFTMarketCore.sol";
import "./NFTMarketCreators.sol";

error SendValueWithFallbackWithdraw_No_Funds_Available();

/**
 * @title A mixin for sending ETH with a fallback withdraw mechanism.
 * @notice Attempt to send ETH and if the transfer fails or runs out of gas, store the balance
 * in the FETH token contract for future withdrawal instead.
 * @dev This mixin was recently switched to escrow funds in FETH.
 * Once we have confirmed all pending balances have been withdrawn, we can remove the escrow tracking here.
 */
abstract contract SendValueWithFallbackWithdraw is
  FoundationTreasuryNode,
  NFTMarketCore,
  ReentrancyGuardUpgradeable,
  // This is not used directly, but required for the linearization of NFTMarketFees.
  NFTMarketCreators
{
  using AddressUpgradeable for address payable;

  /// @dev Tracks the amount of ETH that is stored in escrow for future withdrawal.
  mapping(address => uint256) private __gap_was_pendingWithdrawals;

  /**
   * @notice Emitted when escrowed funds are withdrawn to FETH.
   * @param user The account which has withdrawn ETH.
   * @param amount The amount of ETH which has been withdrawn.
   */
  event WithdrawalToFETH(address indexed user, uint256 amount);

  /**
   * @dev Attempt to send a user or contract ETH and
   * if it fails store the amount owned for later withdrawal in FETH.
   */
  function _sendValueWithFallbackWithdraw(
    address payable user,
    uint256 amount,
    uint256 gasLimit
  ) internal {
    if (amount == 0) {
      return;
    }
    // Cap the gas to prevent consuming all available gas to block a tx from completing successfully
    // solhint-disable-next-line avoid-low-level-calls
    (bool success, ) = user.call{ value: amount, gas: gasLimit }("");
    if (!success) {
      // Store the funds that failed to send for the user in the FETH token
      feth.depositFor{ value: amount }(user);
      emit WithdrawalToFETH(user, amount);
    }
  }

  function _trySendValue(
    address payable user,
    uint256 amount,
    uint256 gasLimit
  ) internal returns (bool success) {
    if (amount == 0) {
      return false;
    }
    // Cap the gas to prevent consuming all available gas to block a tx from completing successfully
    // solhint-disable-next-line avoid-low-level-calls
    (success, ) = user.call{ value: amount, gas: gasLimit }("");
  }

  /**
   * @notice This empty reserved space is put in place to allow future versions to add new
   * variables without shifting down storage in the inheritance chain.
   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
   */
  uint256[499] private __gap;
}

File 15 of 29 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 16 of 29 : IAdminRole.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

/**
 * @notice Interface for AdminRole which wraps the default admin role from
 * OpenZeppelin's AccessControl for easy integration.
 */
interface IAdminRole {
  function isAdmin(address account) external view returns (bool);
}

File 17 of 29 : IOperatorRole.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

/**
 * @notice Interface for OperatorRole which wraps a role from
 * OpenZeppelin's AccessControl for easy integration.
 */
interface IOperatorRole {
  function isOperator(address account) external view returns (bool);
}

File 18 of 29 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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 be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

    /**
     * @dev 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 19 of 29 : IFethMarket.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

/**
 * @notice Interface for functions the market uses in FETH.
 */
interface IFethMarket {
  function depositFor(address account) external payable;

  function marketLockupFor(address account, uint256 amount) external payable returns (uint256 expiration);

  function marketWithdrawFrom(address from, uint256 amount) external;

  function marketWithdrawLocked(
    address account,
    uint256 expiration,
    uint256 amount
  ) external;

  function marketUnlockFor(
    address account,
    uint256 expiration,
    uint256 amount
  ) external;

  function marketChangeLockup(
    address unlockFrom,
    uint256 unlockExpiration,
    uint256 unlockAmount,
    address depositFor,
    uint256 depositAmount
  ) external payable returns (uint256 expiration);
}

File 20 of 29 : 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 21 of 29 : ERC165Checker.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.2.0/contracts/utils/introspection/ERC165.sol
 * Modified to allow checking multiple interfaces w/o checking general 165 support.
 */

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

/**
 * @title Library to query ERC165 support.
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library OZERC165Checker {
  // As per the EIP-165 spec, no interface should ever match 0xffffffff
  bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

  /**
   * @dev Returns true if `account` supports the {IERC165} interface,
   */
  function supportsERC165(address account) internal view returns (bool) {
    // Any contract that implements ERC165 must explicitly indicate support of
    // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
    return
      supportsERC165Interface(account, type(IERC165).interfaceId) &&
      !supportsERC165Interface(account, _INTERFACE_ID_INVALID);
  }

  /**
   * @dev Returns true if `account` supports the interface defined by
   * `interfaceId`. Support for {IERC165} itself is queried automatically.
   *
   * See {IERC165-supportsInterface}.
   */
  function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
    // query support of both ERC165 as per the spec and support of _interfaceId
    return supportsERC165(account) && supportsERC165Interface(account, interfaceId);
  }

  /**
   * @dev Returns a boolean array where each value corresponds to the
   * interfaces passed in and whether they're supported or not. This allows
   * you to batch check interfaces for a contract where your expectation
   * is that some interfaces may not be supported.
   *
   * See {IERC165-supportsInterface}.
   *
   * _Available since v3.4._
   */
  function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) {
    // an array of booleans corresponding to interfaceIds and whether they're supported or not
    bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

    // query support of ERC165 itself
    if (supportsERC165(account)) {
      // query support of each interface in interfaceIds
      unchecked {
        for (uint256 i = 0; i < interfaceIds.length; ++i) {
          interfaceIdsSupported[i] = supportsERC165Interface(account, interfaceIds[i]);
        }
      }
    }

    return interfaceIdsSupported;
  }

  /**
   * @dev Returns true if `account` supports all the interfaces defined in
   * `interfaceIds`. Support for {IERC165} itself is queried automatically.
   *
   * Batch-querying can lead to gas savings by skipping repeated checks for
   * {IERC165} support.
   *
   * See {IERC165-supportsInterface}.
   */
  function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
    // query support of ERC165 itself
    if (!supportsERC165(account)) {
      return false;
    }

    // query support of each interface in _interfaceIds
    unchecked {
      for (uint256 i = 0; i < interfaceIds.length; ++i) {
        if (!supportsERC165Interface(account, interfaceIds[i])) {
          return false;
        }
      }
    }

    // all interfaces supported
    return true;
  }

  /**
   * @notice Query if a contract implements an interface, does not check ERC165 support
   * @param account The address of the contract to query for support of an interface
   * @param interfaceId The interface identifier, as specified in ERC-165
   * @return true if the contract at account indicates support of the interface with
   * identifier interfaceId, false otherwise
   * @dev Assumes that account contains a contract that supports ERC165, otherwise
   * the behavior of this method is undefined. This precondition can be checked
   * with {supportsERC165}.
   * Interface identification is specified in ERC-165.
   */
  function supportsERC165Interface(address account, bytes4 interfaceId) internal view returns (bool) {
    bytes memory encodedParams = abi.encodeWithSelector(IERC165(account).supportsInterface.selector, interfaceId);
    (bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams);
    if (result.length < 32) return false;
    return success && abi.decode(result, (bool));
  }
}

File 22 of 29 : IGetFees.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

/**
 * @notice An interface for communicating fees to 3rd party marketplaces.
 * @dev Originally implemented in mainnet contract 0x44d6e8933f8271abcf253c72f9ed7e0e4c0323b3
 */
interface IGetFees {
  function getFeeRecipients(uint256 id) external view returns (address payable[] memory);

  function getFeeBps(uint256 id) external view returns (uint256[] memory);
}

File 23 of 29 : IGetRoyalties.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

interface IGetRoyalties {
  function getRoyalties(uint256 tokenId)
    external
    view
    returns (address payable[] memory recipients, uint256[] memory feesInBasisPoints);
}

File 24 of 29 : IOwnable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IOwnable {
  /**
   * @dev Returns the address of the current owner.
   */
  function owner() external view returns (address);
}

File 25 of 29 : IRoyaltyInfo.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

/**
 * @notice Interface for EIP-2981: NFT Royalty Standard.
 * For more see: https://eips.ethereum.org/EIPS/eip-2981.
 */
interface IRoyaltyInfo {
  /// @notice Called with the sale price to determine how much royalty
  //          is owed and to whom.
  /// @param _tokenId - the NFT asset queried for royalty information
  /// @param _salePrice - the sale price of the NFT asset specified by _tokenId
  /// @return receiver - address of who should be sent the royalty payment
  /// @return royaltyAmount - the royalty payment amount for _salePrice
  function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
    external
    view
    returns (address receiver, uint256 royaltyAmount);
}

File 26 of 29 : ITokenCreator.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

interface ITokenCreator {
  function tokenCreator(uint256 tokenId) external view returns (address payable);
}

File 27 of 29 : IRoyaltyRegistry.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

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

/**
 * @dev Royalty registry interface
 */
interface IRoyaltyRegistry is IERC165 {

     event RoyaltyOverride(address owner, address tokenAddress, address royaltyAddress);

    /**
     * Override the location of where to look up royalty information for a given token contract.
     * Allows for backwards compatibility and implementation of royalty logic for contracts that did not previously support them.
     * 
     * @param tokenAddress    - The token address you wish to override
     * @param royaltyAddress  - The royalty override address
     */
    function setRoyaltyLookupAddress(address tokenAddress, address royaltyAddress) external;

    /**
     * Returns royalty address location.  Returns the tokenAddress by default, or the override if it exists
     *
     * @param tokenAddress    - The token address you are looking up the royalty for
     */
    function getRoyaltyLookupAddress(address tokenAddress) external view returns(address);

    /**
     * Whether or not the message sender can override the royalty address for the given token address
     *
     * @param tokenAddress    - The token address you are looking up the royalty for
     */
    function overrideAllowed(address tokenAddress) external view returns(bool);
}

File 28 of 29 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 29 of 29 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1337
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address payable","name":"treasury","type":"address"},{"internalType":"address","name":"feth","type":"address"},{"internalType":"address","name":"royaltyRegistry","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"address","name":"marketProxyAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FoundationTreasuryNode_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"FoundationTreasuryNode_Caller_Not_Admin","type":"error"},{"inputs":[{"internalType":"uint256","name":"buyPrice","type":"uint256"}],"name":"NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Cannot_Buy_Unset_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Cannot_Cancel_Unset_Price","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketBuyPrice_Only_Owner_Can_Set_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Price_Already_Set","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Price_Too_High","type":"error"},{"inputs":[{"internalType":"address","name":"seller","type":"address"}],"name":"NFTMarketBuyPrice_Seller_Mismatch","type":"error"},{"inputs":[],"name":"NFTMarketCore_FETH_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTMarketCore_Only_FETH_Can_Transfer_ETH","type":"error"},{"inputs":[],"name":"NFTMarketCore_Seller_Not_Found","type":"error"},{"inputs":[],"name":"NFTMarketCreators_Address_Does_Not_Support_IRoyaltyRegistry","type":"error"},{"inputs":[],"name":"NFTMarketOffer_Cannot_Be_Made_While_In_Auction","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentOfferAmount","type":"uint256"}],"name":"NFTMarketOffer_Offer_Below_Min_Amount","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"NFTMarketOffer_Offer_Expired","type":"error"},{"inputs":[{"internalType":"address","name":"currentOfferFrom","type":"address"}],"name":"NFTMarketOffer_Offer_From_Does_Not_Match","type":"error"},{"inputs":[{"internalType":"uint256","name":"minOfferAmount","type":"uint256"}],"name":"NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount","type":"error"},{"inputs":[],"name":"NFTMarketOffer_Provided_Contract_And_TokenId_Count_Must_Match","type":"error"},{"inputs":[],"name":"NFTMarketOffer_Reason_Required","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Can_Be_Offered_For_24Hrs_Max","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Proxy_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Sale_Expired","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Signature_Canceled_Or_Already_Claimed","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Signature_Verification_Failed","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Too_Much_Value_Provided","type":"error"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"NFTMarketReserveAuction_Already_Listed","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Admin_Cancel_Without_Reason","type":"error"},{"inputs":[{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price","type":"error"},{"inputs":[{"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Cancel_Nonexistent_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction","type":"error"},{"inputs":[{"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxDuration","type":"uint256"}],"name":"NFTMarketReserveAuction_Exceeds_Max_Duration","type":"error"},{"inputs":[{"internalType":"uint256","name":"extensionDuration","type":"uint256"}],"name":"NFTMarketReserveAuction_Less_Than_Extension_Duration","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price","type":"error"},{"inputs":[{"internalType":"address","name":"seller","type":"address"}],"name":"NFTMarketReserveAuction_Not_Matching_Seller","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketReserveAuction_Only_Owner_Can_Update_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Price_Already_Set","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Too_Much_Value_Provided","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"BuyPriceAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BuyPriceCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BuyPriceInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"BuyPriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyReferrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyReferrerProtocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"buyReferrerSellerFee","type":"uint256"}],"name":"BuyReferralPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"OfferAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"OfferCanceledByAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"OfferInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiration","type":"uint256"}],"name":"OfferMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"PrivateSaleFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"bidder","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"ReserveAuctionBidPlaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"ReserveAuctionCanceledByAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"extensionDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reservePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"address","name":"bidder","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"ReserveAuctionFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"ReserveAuctionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawalToFETH","type":"event"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"offerFrom","type":"address"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"acceptOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"nftContracts","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"string","name":"reason","type":"string"}],"name":"adminCancelOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"string","name":"reason","type":"string"}],"name":"adminCancelReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"buyFromPrivateSale","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"buyFromPrivateSaleFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"buyV2","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"cancelBuyPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"cancelReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"createReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"finalizeReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getBuyPrice","outputs":[{"internalType":"address","name":"seller","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"getFeesAndRecipients","outputs":[{"internalType":"uint256","name":"protocolFee","type":"uint256"},{"internalType":"uint256","name":"creatorRev","type":"uint256"},{"internalType":"address payable[]","name":"creatorRecipients","type":"address[]"},{"internalType":"uint256[]","name":"creatorShares","type":"uint256[]"},{"internalType":"uint256","name":"sellerRev","type":"uint256"},{"internalType":"address payable","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFethAddress","outputs":[{"internalType":"address","name":"fethAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFoundationTreasury","outputs":[{"internalType":"address payable","name":"treasuryAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getMinBidAmount","outputs":[{"internalType":"uint256","name":"minimum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getMinOfferAmount","outputs":[{"internalType":"uint256","name":"minimum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getOffer","outputs":[{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getOfferReferrer","outputs":[{"internalType":"address payable","name":"referrer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getReserveAuction","outputs":[{"components":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address payable","name":"seller","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"extensionDuration","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"address payable","name":"bidder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct NFTMarketReserveAuction.ReserveAuction","name":"auction","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getReserveAuctionBidReferrer","outputs":[{"internalType":"address payable","name":"referrer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getReserveAuctionIdFor","outputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyRegistry","outputs":[{"internalType":"address","name":"registry","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"makeOffer","outputs":[{"internalType":"uint256","name":"expiration","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"makeOfferV2","outputs":[{"internalType":"uint256","name":"expiration","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"placeBid","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"placeBidV2","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"setBuyPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"updateReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101206040523480156200001257600080fd5b5060405162005d7438038062005d74833981016040819052620000359162000417565b808284868862000059816001600160a01b03166200029b60201b62002ba91760201c565b620000775760405163028bba2560e61b815260040160405180910390fd5b6001600160a01b039081166080526200009e9082166200029b602090811b62002ba917901c565b620000bc576040516346b6915b60e11b815260040160405180910390fd5b6001600160a01b0390811660a052620000eb90821663220258eb60e21b620002aa602090811b62002bb817901c565b62000109576040516320af3a1f60e11b815260040160405180910390fd5b6001600160a01b031660c0526305265c00811115620001465760405163ccd285bd60e01b81526305265c0060048201526024015b60405180910390fd5b6103848110156200016f5760405163494c8c0760e11b815261038460048201526024016200013d565b60e052620001926001600160a01b0382166200029b602090811b62002ba917901c565b620001b05760405163037b60fb60e61b815260040160405180910390fd5b604080518082018252600c81526b11939113919513585c9ad95d60a21b6020918201528151808301835260018152603160f81b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527ffbcf34d9e40a15bdeb367420eb7542a501124a2eeb8d040648f646644b4baf2d818401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201526001600160a01b039390931660a0808501919091528251808503909101815260c090930190915281519101206101005250620004f49350505050565b6001600160a01b03163b151590565b6000620002b783620002d4565b8015620002cb5750620002cb83836200030c565b90505b92915050565b6000620002e9826301ffc9a760e01b6200030c565b8015620002ce575062000305826001600160e01b03196200030c565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090620003759086906200048b565b6000604051808303818686fa925050503d8060008114620003b3576040519150601f19603f3d011682016040523d82523d6000602084013e620003b8565b606091505b5091509150602081511015620003d55760009350505050620002ce565b818015620003f4575080806020019051810190620003f49190620004c9565b9695505050505050565b6001600160a01b03811681146200041457600080fd5b50565b600080600080600060a086880312156200043057600080fd5b85516200043d81620003fe565b60208701519095506200045081620003fe565b60408701519094506200046381620003fe565b6060870151608088015191945092506200047d81620003fe565b809150509295509295909350565b6000825160005b81811015620004ae576020818601810151858301520162000492565b81811115620004be576000828501525b509190910192915050565b600060208284031215620004dc57600080fd5b81518015158114620004ed57600080fd5b9392505050565b60805160a05160c05160e051610100516157cb620005a9600039600061259b0152600081816114770152818161202d01526129210152600081816106ac0152613f330152600081816101e1015281816104a901528181610b9c015281816118d301528181611a1e0152818161232501528181612a9801528181612cdf01528181612f4f0152818161368901526146610152600081816106ff0152818161084f0152818161150401526138ce01526157cb6000f3fe6080604052600436106101d15760003560e01c80637de3bd07116100f7578063ac71045e11610095578063b6aff8c111610064578063b6aff8c11461068a578063daa351d41461069d578063e5d1e723146106d0578063f7a2da23146106f057600080fd5b8063ac71045e146105ed578063af1e1de314610632578063b01ef60814610664578063b103d74f1461067757600080fd5b80639979ef45116100d15780639979ef45146104cd5780639e64ba6c146104e05780639e79b41f1461054a578063a59ac6dd146105da57600080fd5b80637de3bd07146104725780638129fc1c14610485578063895633ba1461049a57600080fd5b80634635256e1161016f578063614b151c1161013e578063614b151c1461040c5780636775d96a1461041f5780637430e0c614610432578063798bac8d1461045257600080fd5b80634635256e1461036d57806347e35740146103ac5780634ce6931a146103cc5780635d83d562146103ec57600080fd5b806321561935116101ab578063215619351461029f578063262907c5146102bf57806329e0e160146102fc5780632ab2b52b1461031c57600080fd5b806303ec16d71461023f57806306dcf7481461025f57806321506fff1461027f57600080fd5b3661023a57336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610238576040517f37de3dd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561024b57600080fd5b5061023861025a366004614f62565b610723565b34801561026b57600080fd5b5061023861027a36600461500b565b61083a565b34801561028b57600080fd5b5061023861029a3660046150a5565b610c95565b3480156102ab57600080fd5b506102386102ba3660046150d3565b610eb0565b3480156102cb57600080fd5b506102df6102da3660046150d3565b611025565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561030857600080fd5b506102386103173660046150ff565b6110a6565b34801561032857600080fd5b5061035f6103373660046150d3565b6001600160a01b03909116600090815261177660209081526040808320938352929052205490565b6040519081526020016102f3565b34801561037957600080fd5b5061038d6103883660046150d3565b611243565b604080516001600160a01b0390931683526020830191909152016102f3565b3480156103b857600080fd5b5061035f6103c73660046150a5565b6112b9565b3480156103d857600080fd5b506102386103e7366004615147565b6112f1565b3480156103f857600080fd5b5061023861040736600461517c565b6114ef565b61035f61041a3660046151c8565b61180e565b61023861042d366004615228565b611ba8565b34801561043e57600080fd5b5061023861044d3660046150a5565b611bbf565b34801561045e57600080fd5b5061023861046d366004615147565b611c77565b61035f610480366004615147565b611e97565b34801561049157600080fd5b50610238611ea6565b3480156104a657600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006102df565b6102386104db3660046150a5565b611f76565b3480156104ec57600080fd5b506102df6104fb3660046150a5565b60009081526117776020526040908190206006810154600290910154600160a01b9182900467ffffffffffffffff1691900490911b73ffffffffffffffffffffffff0000000000000000161790565b34801561055657600080fd5b5061056a6105653660046150a5565b611f82565b6040516102f391906000610100820190506001600160a01b0380845116835260208401516020840152806040850151166040840152606084015160608401526080840151608084015260a084015160a08401528060c08501511660c08401525060e083015160e083015292915050565b6102386105e8366004615147565b61207c565b3480156105f957600080fd5b5061060d6106083660046150d3565b61208e565b604080516001600160a01b0390941684526020840192909252908201526060016102f3565b34801561063e57600080fd5b5061065261064d366004615147565b612110565b6040516102f396959493929190615282565b6102386106723660046151c8565b61214e565b610238610685366004615330565b612224565b610238610698366004615395565b612731565b3480156106a957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006102df565b3480156106dc57600080fd5b5061035f6106eb3660046150d3565b612b47565b3480156106fc57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006102df565b808060000361074557604051631d4b87f360e11b815260040160405180910390fd5b60008381526117776020526040902060028101546001600160a01b03163314610797576002810154604051632600954360e21b81526001600160a01b0390911660048201526024015b60405180910390fd5b6005810154156107ba57604051635aea7c4760e01b815260040160405180910390fd5b828160070154036107f7576040517f4b669ac700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007810183905560405183815284907f0c0f2662914f0cd1e952db2aa425901cb00e7c1f507687d22cb04e836d55d9c7906020015b60405180910390a250505050565b604051630935e01b60e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906324d7806c90602401602060405180830381865afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c291906153ce565b6108df5760405163af8db33360e01b815260040160405180910390fd5b6002610b8754036109325760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556000819003610973576040517ffedbcec600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8483146109ac576040517f4716476200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015610c8657600061233760008989858181106109d0576109d06153f0565b90506020020160208101906109e59190615406565b6001600160a01b03166001600160a01b031681526020019081526020016000206000878785818110610a1957610a196153f0565b602090810292909201358352508181019290925260409081016000908120825160a081018452815463ffffffff808216835264010000000082046001600160601b031696830196909652600160801b90046fffffffffffffffffffffffffffffffff1693810193909352600101546001600160a01b0381166060840152600160a01b90049092166080820152915061233790898985818110610abd57610abd6153f0565b9050602002016020810190610ad29190615406565b6001600160a01b03166001600160a01b031681526020019081526020016000206000878785818110610b0657610b066153f0565b602090810292909201358352508101919091526040016000908120908155600101805477ffffffffffffffffffffffffffffffffffffffffffffffff1916905580514263ffffffff90911610610c7d5760608101518151602083015160405163345db49360e01b81526001600160a01b03938416600482015263ffffffff90921660248301526001600160601b031660448201527f00000000000000000000000000000000000000000000000000000000000000009091169063345db49390606401600060405180830381600087803b158015610be257600080fd5b505af1158015610bf6573d6000803e3d6000fd5b50505050858583818110610c0c57610c0c6153f0565b90506020020135888884818110610c2557610c256153f0565b9050602002016020810190610c3a9190615406565b6001600160a01b03167fd3802baab3d80ff411a2f83c8394d07877339ff8352f7c1b02fbcd70ea7cac8c8686604051610c74929190615423565b60405180910390a35b506001016109af565b50506001610b87555050505050565b6002610b875403610ce85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b878190556000828152611777602090815260409182902082516101408101845281546001600160a01b03908116825260018301549382019390935293810154808316938501849052600160a01b908190046001600160601b0316606086015260038201546080860152600482015460a0860152600582015460c0860152600682015492831660e086015290910467ffffffffffffffff16610100840152600701546101208301523314610dc3576040808201519051632600954360e21b81526001600160a01b03909116600482015260240161078e565b60c081015115610de657604051635aea7c4760e01b815260040160405180910390fd5b80516001600160a01b03166000908152611776602090815260408083208285018051855290835281842084905585845261177790925280832080546001600160a01b031916815560018101849055600281018490556003810184905560048101849055600581018490556006810180546001600160e01b0319169055600701929092558251905191830151610e7b9290612bd4565b60405182907f14b9c40404d5b41deb481f9a40b8aeb2bf4b47679b38cf757075a66ed510f7f190600090a250506001610b8755565b6002610b875403610f035760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680610f67576040517fc09f8e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381163314610fb4576040517ff049b41a0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161078e565b6001600160a01b0383166000908152611f4e60209081526040808320858452909152812055610fe4838333612bd4565b60405182906001600160a01b038516907f70c7877531c04c7d9caa8a7eca127384f04e8a6ee58b63f778ce5401d8bcae4190600090a350506001610b875550565b6001600160a01b038216600090815261233760209081526040808320848452909152812080544263ffffffff90911610156110645760009150506110a0565b80546001820154600160801b90910460201b73ffffffffffffffffffffffffffffffff0000000016600160a01b90910463ffffffff16175b9150505b92915050565b6002610b8754036110f95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038416600090815261233760209081526040808320868452909152902080544263ffffffff90911610156111715780546040517f8c9e57cf00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260240161078e565b805464010000000090046001600160601b03168211156111d25780546040517f242373610000000000000000000000000000000000000000000000000000000081526401000000009091046001600160601b0316600482015260240161078e565b60018101546001600160a01b0384811691161461122c5760018101546040517fa7d95dc30000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161078e565b6112368585612bdf565b50506001610b8755505050565b6001600160a01b038083166000908152611f4e60209081526040808320858452909152812054909116908161127b57506000196112b2565b506001600160a01b0383166000908152611f4e60209081526040808320858452909152902054600160a01b90046001600160601b03165b9250929050565b600081815261177760205260408120600581015482036112dd576007015492915050565b6112ea8160070154612e7c565b9392505050565b6002610b8754036113445760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b875580600081900361136d57604051631d4b87f360e11b815260040160405180910390fd5b600061138161138d80546001810190915590565b905061138d8585612e9e565b6001600160a01b03851660009081526117766020908152604080832087845290915290205415611413576001600160a01b038516600090815261177660209081526040808320878452909152908190205490517f7618a003000000000000000000000000000000000000000000000000000000008152600481019190915260240161078e565b6001600160a01b038516600081815261177660209081526040808320888452825280832085905584835261177782529182902080546001600160a01b03199081168517825560018201899055600282018054339216821790556007820188905583517f00000000000000000000000000000000000000000000000000000000000000008152610384938101939093529282018790526060820185905292879290917f1062dd3b35f12b4064331244d00f40c1d4831965e4285654157a2409c6217cff9060800160405180910390a450506001610b875550505050565b604051630935e01b60e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906324d7806c90602401602060405180830381865afa158015611553573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157791906153ce565b6115945760405163af8db33360e01b815260040160405180910390fd5b6002610b8754036115e75760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556000819003611628576040517ffbaca1c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526117776020908152604080832081516101408101835281546001600160a01b039081168252600183015494820194909452600282015480851693820193909352600160a01b928390046001600160601b0316606082015260038201546080820152600482015460a0820152600582015460c0820152600682015493841660e08201529190920467ffffffffffffffff16610100820152600790910154610120820181905290910361170a576040517fe3a2ab0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516001600160a01b03166000908152611776602090815260408083208285018051855290835281842084905587845261177790925280832080546001600160a01b031916815560018101849055600281018490556003810184905560048101849055600581018490556006810180546001600160e01b031916905560070192909255825190519183015161179f9290612bd4565b60e08101516001600160a01b0316156117c8576117c88160e00151826101200151614e20612eac565b837f1d56d378404d81e3fc5f3dfbf88359b8cb2ecafa73b3270c478bf7b2bdd1446984846040516117fa929190615423565b60405180910390a250506001610b87555050565b600061181b858585612fee565b1561182857506000611ba0565b611832858561305c565b15611869576040517f83a483f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516600090815261233760209081526040808320878452909152902080544263ffffffff9091161015611951576040517f4ec58ed7000000000000000000000000000000000000000000000000000000008152336004820152602481018590527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690634ec58ed790349060440160206040518083038185885af1158015611925573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061194a9190615452565b9150611a93565b80546000906119709064010000000090046001600160601b0316612e7c565b9050808510156119af576040517fe40a30e60000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b600182015482546040517f5fdec5610000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015263ffffffff821660248201526401000000009091046001600160601b03166044820152336064820152608481018790527f000000000000000000000000000000000000000000000000000000000000000090911690635fdec56190349060a40160206040518083038185885af1158015611a6a573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611a8f9190615452565b9250505b600181018054825463ffffffff8581167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909216919091176401000000006001600160601b03891602176fffffffffffffffffffffffffffffffff908116602088811c909216600160801b0217855577ffffffffffffffffffffffffffffffffffffffffffffffff19909216337fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff811691909117600160a01b92881692909202919091179092556040805187815291820185905287916001600160a01b038a16917ece0a712e4e277ac7b34942865f0de7a5629dffe0539b70423ad5ff1ed6ab42910160405180910390a4505b949350505050565b611bb786863487878787612224565b505050505050565b6002610b875403611c125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755600081815261177760205260408120600501549003611c63576040517f4b6ad8fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c6e8160006130a8565b506001610b8755565b6002610b875403611cca5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755611cdb8383836132e1565b611e8c576001600160601b03811115611d20576040517f35ec82cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038381166000908152611f4e60209081526040808320868452909152902080549091811690600160a01b90046001600160601b031683148015611d7257506001600160a01b03811615155b15611da9576040517fb6950f3600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546001600160601b038416600160a01b026001600160a01b039182161783558116611dee57611dd98585612e9e565b81546001600160a01b03191633178255611e3b565b6001600160a01b0381163314611e3b576040517f697d918e0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161078e565b336001600160a01b031684866001600160a01b03167ffcc77ea8bdcce862f43b7fb00fe6b0eb90d6aeead27d3800d9257cf7a05f9d9686604051611e8191815260200190565b60405180910390a450505b50506001610b875550565b6000611ba0848484600061180e565b600054610100900460ff16611ec15760005460ff1615611ec5565b303b155b611f375760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161078e565b600054610100900460ff16158015611f59576000805461ffff19166101011790555b611f61613346565b8015611f73576000805461ff00191690555b50565b611f7381346000612731565b611fe560405180610100016040528060006001600160a01b031681526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600081525090565b506000908152611777602090815260409182902082516101008101845281546001600160a01b03908116825260018301549382019390935260028201548316938101939093527f000000000000000000000000000000000000000000000000000000000000000060608401526103846080840152600581015460a0840152600681015490911660c08301526007015460e082015290565b612089838383600061214e565b505050565b6001600160a01b03821660009081526123376020908152604080832084845290915281208054829182914263ffffffff90911610156120d857600080600093509350935050612109565b600181015490546001600160a01b03909116935063ffffffff8116925064010000000090046001600160601b031690505b9250925092565b60008060608060008060006121258a8a6133cb565b90506121358a8a838b60006133d7565b50949f919e50929c50909a509198509650945050505050565b6001600160a01b0384166000908152611f4e6020908152604080832086845290915290208054600160a01b90046001600160601b03168310156121d15780546040517f16b5016f000000000000000000000000000000000000000000000000000000008152600160a01b9091046001600160601b0316600482015260240161078e565b80546001600160a01b0316612212576040517fda48e18400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61221d858584613585565b5050505050565b6002610b8754036122775760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755428410156122b7576040517f98dbee1b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b426202a300018411156122f6576040517f3807938200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61230087876137bd565b3485111561238e5760405163452f2b8f60e01b815233600482015234860360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063452f2b8f90604401600060405180830381600087803b15801561237157600080fd5b505af1158015612385573d6000803e3d6000fd5b505050506123c8565b348510156123c8576040517ff8c4782000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516331a9108f60e11b8152600481018790526000906001600160a01b03891690636352211e90602401602060405180830381865afa158015612410573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612434919061546b565b6001600160a01b03808a166000908152611b66602090815260408083208c84528252808320338452825280832093851683529281528282208a835281528282208983529052205490915060ff16156124b8576040517fd0be1ad300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038881166000818152611b66602090815260408083208c845282528083203380855290835281842095871684529482528083208b845282528083208a84528252808320805460ff1916600117905580517f6f5a5497fcb7364f6bad56db9aad5785b6786717424e748b8bfef6e6554cd5518184015280820194909452606084018c9052608084019490945260a083018a905260c08084018a90528451808503909101815260e0840190945283519301929092207f19010000000000000000000000000000000000000000000000000000000000006101008301527f00000000000000000000000000000000000000000000000000000000000000006101028301526101228201526101420160408051601f19818403018152919052805160209091012090506001600160a01b0382166125fa828787876137fb565b6001600160a01b03161461263a576040517f3902771600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506040516323b872dd60e01b81526001600160a01b038281166004830152336024830152604482018990528916906323b872dd90606401600060405180830381600087803b15801561268b57600080fd5b505af115801561269f573d6000803e3d6000fd5b5050505060008060006126b68b8b868c6000613823565b604080513381526020810185905290810183905260608101829052608081018c905292955090935091506001600160a01b03808616918c918e16907f6c623fa5e13aaaf28288f807e5b4f9ec6fb7ef812568e00317c552663bea918f9060a00160405180910390a450506001610b8755505050505050505050565b6002610b8754036127845760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b875560008381526117776020526040812060078101549091036127d7576040517f125197d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34831015612811576040517fe2bbc1e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058101546001600160a01b03831615158061282c57508015155b1561289a576002820180546001600160a01b0316604085901c6001600160601b0316600160a01b908102919091179091556006830180547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1667ffffffffffffffff86169092029190911790555b8060000361294d5781600701548410156128e85781600701546040517f31e6f71c00000000000000000000000000000000000000000000000000000000815260040161078e91815260200190565b81546001830154612902916001600160a01b031690613a82565b50600781018390556006810180546001600160a01b03191633179055427f00000000000000000000000000000000000000000000000000000000000000000160058201819055612a73565b4281101561298a576040517f3feeb88d0000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b6006820154336001600160a01b03909116036129d2576040517fe140576800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006129e18360070154612e7c565b905080851015612a20576040517fcd698a190000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b50600782018054600684018054928790556001600160a01b0319831633179055906001600160a01b0316426103840180841015612a6257600585018190559250825b50612a708183614e20612eac565b50505b34841115612afd5760405163452f2b8f60e01b815233600482015234850360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063452f2b8f90604401600060405180830381600087803b158015612ae457600080fd5b505af1158015612af8573d6000803e3d6000fd5b505050505b6040805185815260208101839052339187917f26ea3ebbda62eb1baef13e1c237dddd956c87f80b2801f2616d806d52557b121910160405180910390a350506001610b8755505050565b6001600160a01b038216600090815261233760209081526040808320848452909152812080544263ffffffff90911610612b9f578054612b979064010000000090046001600160601b0316612e7c565b9150506110a0565b5060019392505050565b6001600160a01b03163b151590565b6000612bc383613a8c565b80156112ea57506112ea8383613abf565b612089838383613bbd565b6001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046001600160601b03908116848901908152600160801b9093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c52979099529890945577ffffffffffffffffffffffffffffffffffffffffffffffff1990961690965591518251955193517f4dc8fb3c000000000000000000000000000000000000000000000000000000008152908716600482015294909316602485015291166044830152917f00000000000000000000000000000000000000000000000000000000000000001690634dc8fb3c90606401600060405180830381600087803b158015612d2357600080fd5b505af1158015612d37573d6000803e3d6000fd5b5050505060608101516040516323b872dd60e01b81523360048201526001600160a01b03918216602482015260448101849052908416906323b872dd90606401600060405180830381600087803b158015612d9157600080fd5b505af1925050508015612da2575060015b612db657612db68383836060015133613bf4565b6000806000612e0c86863387602001516001600160601b0316612e0789604001518a6080015173ffffffffffffffffffffffffffffffff0000000060209290921b9190911663ffffffff9091161790565b613823565b606080880151604080513381526020810187905290810185905291820183905293965091945092506001600160a01b03918216918791908916907f1cb8adb37d6d35e94cd0695ca39895b84371864713f5ca7eada52af9ff23744b906080015b60405180910390a4505050505050565b6000600a8204808203612e94576112ea83600161549e565b6112ea838261549e565b612ea88282613c00565b5050565b81600003612eb957505050565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d8060008114612f09576040519150601f19603f3d011682016040523d82523d6000602084013e612f0e565b606091505b5050905080612fe8576040517faa67c9190000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063aa67c9199085906024016000604051808303818588803b158015612f9457600080fd5b505af1158015612fa8573d6000803e3d6000fd5b5050505050836001600160a01b03167fa2201512569adb2d513531dfd69b66df50bd5cffb8c1bbe65a4611f9e1eadbd18460405161082c91815260200190565b50505050565b6001600160a01b038084166000908152611f4e60209081526040808320868452909152812080549192909116158061303657508054600160a01b90046001600160601b031683105b156130455760009150506112ea565b61305185856000613585565b506001949350505050565b6001600160a01b038216600090815261177660209081526040808320848452909152812054801580159061109c5750600090815261177760205260409020600501544211159392505050565b6000828152611777602090815260409182902082516101408101845281546001600160a01b039081168252600183015493820193909352600282015480841694820194909452600160a01b938490046001600160601b0316606082015260038201546080820152600482015460a0820152600582015460c08201819052600683015493841660e08301529390920467ffffffffffffffff1661010083015260070154610120820152904211613191578060c001516040517f3a017f6000000000000000000000000000000000000000000000000000000000815260040161078e91815260200190565b80516001600160a01b03166000908152611776602090815260408083208285015184528252808320839055858352611777909152812080546001600160a01b031916815560018101829055600281018290556003810182905560048101829055600581018290556006810180546001600160e01b0319169055600701558161322c5761322c816000015182602001518360e001516000613c6a565b600080600061327a84600001518560200151866040015187610120015188610100015167ffffffffffffffff1660408a606001516001600160601b03166001600160a01b0316901b17613823565b9250925092508360e001516001600160a01b031684604001516001600160a01b0316877f2edb0e99c6ac35be6731dab554c1d1fa1b7beb675090dbb09fb14e615aca1c4a868686604051612e6c939291909283526020830191909152604082015260600190565b6001600160a01b038316600090815261233760209081526040808320858452909152812080544263ffffffff909116108061332d5750805464010000000090046001600160601b031683115b1561333c5760009150506112ea565b6130518585612bdf565b600054610100900460ff166133c35760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161078e565b600161138d55565b60006112ea8383613d19565b600060608060008060008060008c6001600160a01b03166340c1a064619c408e6040518363ffffffff1660e01b815260040161341591815260200190565b6020604051808303818786fa9350505050801561344f575060408051601f3d908101601f1916820190925261344c9181019061546b565b60015b156134575790505b6134618d8d613d4f565b8151919850965060148b0490156134fd57816001600160a01b03168c6001600160a01b031614806134c95750875160011480156134c95750876000815181106134ac576134ac6153f0565b60200260200101516001600160a01b03168c6001600160a01b0316145b156134d857808b039550613506565b600a8b0495508b9450856134ec828d6154b6565b6134f691906154b6565b9350613506565b8b9450808b0393505b6001600160a01b038a161580159061352757506001600160a01b038a163314155b801561354557508b6001600160a01b03168a6001600160a01b031614155b80156135635750816001600160a01b03168a6001600160a01b031614155b1561356f576005810492505b82810398505050959b949a509550955095509550565b6002610b8754036135d85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038381166000908152611f4e60209081526040808320868452808352818420825180840190935280549586168352600160a01b9095046001600160601b031682840152868452909152915561363a84846137bd565b3481602001516001600160601b031611156136eb57602081015160405163452f2b8f60e01b8152336004820152346001600160601b039092169190910360248201819052906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063452f2b8f90604401600060405180830381600087803b1580156136cd57600080fd5b505af11580156136e1573d6000803e3d6000fd5b505050505061371b565b3481602001516001600160601b0316101561371b57602081015161371b9033906001600160601b0316340361442e565b6137288484336000613bf4565b600080600061374b8787866000015187602001516001600160601b031689613823565b865160408051338152602081018690529081018490526060810183905293965091945092506001600160a01b039081169188918a16907fd28c0a7dd63bc853a4e36306655da9f8c0b29ff9d0605bb976ae420e46a999309060800160405180910390a450506001610b87555050505050565b6001600160a01b0380831660009081526123376020908152604080832085845290915290206001810154909133911603612089576120898383614547565b600080600061380c878787876146f8565b91509150613819816147e5565b5095945050505050565b6000806000606080600080600061383d8d8d8d8d8d6133d7565b8681019e50929c509a509298509096509094509250905081156138c9576138678983614e2061499b565b156138c457604080516001600160a01b038b81168252602082018590526000928201929092528d918f16907f141b92fd9766c80ab120598ea2f6be9802470ec59b5446dd9bf46214ead8d08e9060600160405180910390a36138c9565b600091015b6138f67f000000000000000000000000000000000000000000000000000000000000000082614e20612eac565b8615613a655784516001811115613a3b576005811115613914575060055b6000805b8281101561397557612710878281518110613935576139356153f0565b6020026020010151111561394c5760019250613975565b86818151811061395e5761395e6153f0565b602002602001015182019150806001019050613918565b508060000361398357600191505b600060015b83811015613a00576000838983815181106139a5576139a56153f0565b60200260200101518d6139b891906154cd565b6139c291906154ec565b90506139ce818461549e565b92506139f78a83815181106139e5576139e56153f0565b60200260200101518262033450612eac565b50600101613988565b50613a3488600081518110613a1757613a176153f0565b6020026020010151828c613a2b91906154b6565b62033450612eac565b5050613a63565b613a6386600081518110613a5157613a516153f0565b60200260200101518962033450612eac565b505b613a728387614e20612eac565b5050505050955095509592505050565b612ea88282614a09565b6000613a9f826301ffc9a760e01b613abf565b80156110a05750613ab8826001600160e01b0319613abf565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090613b3b90869061550e565b6000604051808303818686fa925050503d8060008114613b77576040519150601f19603f3d011682016040523d82523d6000602084013e613b7c565b606091505b5091509150602081511015613b9757600093505050506110a0565b818015613bb3575080806020019051810190613bb391906153ce565b9695505050505050565b6001600160a01b038084166000908152611f4e602090815260408083208684529091529020541680612fe857612fe8848484614a1d565b612fe884848484614a53565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680613c36576120898383614ad7565b6001600160a01b0381163314612089576040516332f3b03360e01b81526001600160a01b038216600482015260240161078e565b6001600160a01b03811615613cab576040517f57a016b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516323b872dd60e01b81523060048201526001600160a01b038381166024830152604482018590528516906323b872dd90606401600060405180830381600087803b158015613cfb57600080fd5b505af1158015613d0f573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038083166000908152611f4e6020908152604080832085845290915290205416806110a0576112ea8383614bb5565b606080613d6c6001600160a01b03851663152a902d60e11b613abf565b15613e475760405163152a902d60e11b81526004810184905261271060248201526001600160a01b03851690632a55205a90619c409060440160408051808303818786fa93505050508015613dde575060408051601f3d908101601f19168201909252613ddb91810190615549565b60015b15613e47578015613e445760408051600180825281830190925290602080830190803683370190505093508184600081518110613e1d57613e1d6153f0565b60200260200101906001600160a01b031690816001600160a01b03168152505050506112b2565b50505b613e616001600160a01b038516635d9dd7eb60e11b613abf565b15613efb57604051635d9dd7eb60e11b8152600481018490526001600160a01b0385169063bb3bafd690619c40906024016000604051808303818786fa93505050508015613ed157506040513d6000823e601f3d908101601f19168201604052613ece91908101906156b1565b60015b15613efb5781518015801590613ee75750815181145b15613ef7575090925090506112b2565b5050505b6040517fde5488af0000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063de5488af90619c40906024016020604051808303818786fa93505050508015613f9b575060408051601f3d908101601f19168201909252613f989181019061546b565b60015b1561416b57846001600160a01b0316816001600160a01b03161461416957935083613fd66001600160a01b03821663152a902d60e11b613abf565b156140a95760405163152a902d60e11b81526004810185905261271060248201526001600160a01b03861690632a55205a90619c409060440160408051808303818786fa93505050508015614048575060408051601f3d908101601f1916820190925261404591810190615549565b60015b156140a95760408051600180825281830190925290602080830190803683370190505094508185600081518110614081576140816153f0565b60200260200101906001600160a01b031690816001600160a01b0316815250505050506112b2565b82511580156140cd57506140cd6001600160a01b038616635d9dd7eb60e11b613abf565b1561416957604051635d9dd7eb60e11b8152600481018590526001600160a01b0386169063bb3bafd690619c40906024016000604051808303818786fa9350505050801561413d57506040513d6000823e601f3d908101601f1916820160405261413a91908101906156b1565b60015b1561416957815180158015906141535750815181145b15614165575090935091506112b29050565b5050505b505b61419e6001600160a01b0385167fb779958400000000000000000000000000000000000000000000000000000000613abf565b156142d7576040517fb9c4d9fb000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0385169063b9c4d9fb90619c40906024016000604051808303818786fa9350505050801561422757506040513d6000823e601f3d908101601f191682016040526142249190810190615715565b60015b156142d757805180156142d4576040517f0ebd4c7f000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03871690630ebd4c7f90619c40906024016000604051808303818786fa935050505080156142b857506040513d6000823e601f3d908101601f191682016040526142b5919081019061574a565b60015b156142d457805182036142d2579193509091506112b29050565b505b50505b6040517f40c1a064000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b038516906340c1a06490619c40906024016020604051808303818786fa93505050508015614356575060408051601f3d908101601f191682019092526143539181019061546b565b60015b156143c45760015b604051908082528060200260200182016040528015614387578160200160208202803683370190505b509250808360008151811061439e5761439e6153f0565b60200260200101906001600160a01b031690816001600160a01b031681525050506112b2565b836001600160a01b0316638da5cb5b619c406040518263ffffffff1660e01b81526004016020604051808303818786fa93505050508015614422575060408051601f3d908101601f1916820190925261441f9181019061546b565b60015b156112b257600161435e565b8047101561447e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161078e565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146144cb576040519150601f19603f3d011682016040523d82523d6000602084013e6144d0565b606091505b50509050806120895760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161078e565b6001600160a01b0382166000908152612337602090815260408083208484529091529020544263ffffffff90911610612ea8576001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046001600160601b03908116848901908152600160801b9093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c52979099529890945577ffffffffffffffffffffffffffffffffffffffffffffffff19909616909655915182519551935163345db49360e01b8152908716600482015294909316602485015291166044830152917f0000000000000000000000000000000000000000000000000000000000000000169063345db49390606401600060405180830381600087803b1580156146a557600080fd5b505af11580156146b9573d6000803e3d6000fd5b50506040518492506001600160a01b03861691507f30c264456cbd17f5f67d7534654161414f34c0e6cc1b7500e169b7a7aea4afc090600090a3505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561472f57506000905060036147dc565b8460ff16601b1415801561474757508460ff16601c14155b1561475857506000905060046147dc565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156147ac573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166147d5576000600192509250506147dc565b9150600090505b94509492505050565b60008160048111156147f9576147f961577f565b036148015750565b60018160048111156148155761481561577f565b036148625760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161078e565b60028160048111156148765761487661577f565b036148c35760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161078e565b60038160048111156148d7576148d761577f565b0361492f5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161078e565b60048160048111156149435761494361577f565b03611f735760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161078e565b6000826000036149ad575060006112ea565b6040516001600160a01b03851690839085906000818181858888f193505050503d80600081146149f9576040519150601f19603f3d011682016040523d82523d6000602084013e6149fe565b606091505b509095945050505050565b614a138282614547565b612ea88282614bf9565b6001600160a01b038316600090815261177660209081526040808320858452909152812054900361208957612089838383614c32565b6001600160a01b038085166000908152611f4e60209081526040808320878452909152902054168015614acb57816001600160a01b0316816001600160a01b031614614abd576040516332f3b03360e01b81526001600160a01b038216600482015260240161078e565b60009150614acb8585614c9f565b61221d85858585614cf2565b6001600160a01b03821660009081526117766020908152604080832084845290915281205490819003614b0e576120898383614e94565b6000818152611777602052604081206005810154909103614b6c5760028101546001600160a01b03163314614b67576002810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b612fe8565b60068101546001600160a01b03163314614baa576006810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b612fe88260016130a8565b6001600160a01b03808316600090815261177660209081526040808320858452825280832054835261177790915290206002015416806110a0576112ea8383614ef6565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020805490911615612089576120898383614c9f565b6040516323b872dd60e01b81523060048201526001600160a01b038281166024830152604482018490528416906323b872dd90606401600060405180830381600087803b158015614c8257600080fd5b505af1158015614c96573d6000803e3d6000fd5b50505050505050565b6001600160a01b0382166000818152611f4e60209081526040808320858452909152808220829055518392917faa6271d89a385571e237d3e7254ccc7c09f68055e6e9b410ed08233a8b9a05cf91a35050565b6001600160a01b0384166000908152611776602090815260408083208684529091529020548015614e88576000818152611777602052604081206005810154909103614e36576001600160a01b03831615801590614d60575060028101546001600160a01b03848116911614155b15614d8f576002810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b6001600160a01b038616600090815261177660209081526040808320888452825280832083905584835261177790915280822080546001600160a01b031916815560018101839055600281018390556003810183905560048101839055600581018390556006810180546001600160e01b03191690556007018290555183917f5603897cc9b1e866f3f7395ffc6638776041f21c094d0b4e748ff44c407fa36291a2614e82565b60068101546001600160a01b03848116911614614e77576006810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b614e828260016130a8565b60009250505b61221d85858585613c6a565b6040516323b872dd60e01b8152336004820152306024820152604481018290526001600160a01b038316906323b872dd90606401600060405180830381600087803b158015614ee257600080fd5b505af1158015611bb7573d6000803e3d6000fd5b6040516331a9108f60e11b8152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa158015614f3e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ea919061546b565b60008060408385031215614f7557600080fd5b50508035926020909101359150565b60008083601f840112614f9657600080fd5b50813567ffffffffffffffff811115614fae57600080fd5b6020830191508360208260051b85010111156112b257600080fd5b60008083601f840112614fdb57600080fd5b50813567ffffffffffffffff811115614ff357600080fd5b6020830191508360208285010111156112b257600080fd5b6000806000806000806060878903121561502457600080fd5b863567ffffffffffffffff8082111561503c57600080fd5b6150488a838b01614f84565b9098509650602089013591508082111561506157600080fd5b61506d8a838b01614f84565b9096509450604089013591508082111561508657600080fd5b5061509389828a01614fc9565b979a9699509497509295939492505050565b6000602082840312156150b757600080fd5b5035919050565b6001600160a01b0381168114611f7357600080fd5b600080604083850312156150e657600080fd5b82356150f1816150be565b946020939093013593505050565b6000806000806080858703121561511557600080fd5b8435615120816150be565b9350602085013592506040850135615137816150be565b9396929550929360600135925050565b60008060006060848603121561515c57600080fd5b8335615167816150be565b95602085013595506040909401359392505050565b60008060006040848603121561519157600080fd5b83359250602084013567ffffffffffffffff8111156151af57600080fd5b6151bb86828701614fc9565b9497909650939450505050565b600080600080608085870312156151de57600080fd5b84356151e9816150be565b935060208501359250604085013591506060850135615207816150be565b939692955090935050565b803560ff8116811461522357600080fd5b919050565b60008060008060008060c0878903121561524157600080fd5b863561524c816150be565b9550602087013594506040870135935061526860608801615212565b92506080870135915060a087013590509295509295509295565b600060c082018883526020888185015260c0604085015281885180845260e086019150828a01935060005b818110156152d25784516001600160a01b0316835293830193918301916001016152ad565b50508481036060860152875180825290820192508188019060005b81811015615309578251855293830193918301916001016152ed565b50505050608083018590526001600160a01b03841660a08401529050979650505050505050565b600080600080600080600060e0888a03121561534b57600080fd5b8735615356816150be565b965060208801359550604088013594506060880135935061537960808901615212565b925060a0880135915060c0880135905092959891949750929550565b6000806000606084860312156153aa57600080fd5b833592506020840135915060408401356153c3816150be565b809150509250925092565b6000602082840312156153e057600080fd5b815180151581146112ea57600080fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561541857600080fd5b81356112ea816150be565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60006020828403121561546457600080fd5b5051919050565b60006020828403121561547d57600080fd5b81516112ea816150be565b634e487b7160e01b600052601160045260246000fd5b600082198211156154b1576154b1615488565b500190565b6000828210156154c8576154c8615488565b500390565b60008160001904831182151516156154e7576154e7615488565b500290565b60008261550957634e487b7160e01b600052601260045260246000fd5b500490565b6000825160005b8181101561552f5760208186018101518583015201615515565b8181111561553e576000828501525b509190910192915050565b6000806040838503121561555c57600080fd5b8251615567816150be565b6020939093015192949293505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156155b6576155b6615577565b604052919050565b600067ffffffffffffffff8211156155d8576155d8615577565b5060051b60200190565b600082601f8301126155f357600080fd5b81516020615608615603836155be565b61558d565b82815260059290921b8401810191818101908684111561562757600080fd5b8286015b8481101561564b57805161563e816150be565b835291830191830161562b565b509695505050505050565b600082601f83011261566757600080fd5b81516020615677615603836155be565b82815260059290921b8401810191818101908684111561569657600080fd5b8286015b8481101561564b578051835291830191830161569a565b600080604083850312156156c457600080fd5b825167ffffffffffffffff808211156156dc57600080fd5b6156e8868387016155e2565b935060208501519150808211156156fe57600080fd5b5061570b85828601615656565b9150509250929050565b60006020828403121561572757600080fd5b815167ffffffffffffffff81111561573e57600080fd5b61109c848285016155e2565b60006020828403121561575c57600080fd5b815167ffffffffffffffff81111561577357600080fd5b61109c84828501615656565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220042140fd23aa099c914a95e26659cefe3bfa4304f57fce37f994ce9903b4919264736f6c634300080d003300000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb600000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d0000000000000000000000000000000000000000000000000000000000015180000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f

Deployed Bytecode

0x6080604052600436106101d15760003560e01c80637de3bd07116100f7578063ac71045e11610095578063b6aff8c111610064578063b6aff8c11461068a578063daa351d41461069d578063e5d1e723146106d0578063f7a2da23146106f057600080fd5b8063ac71045e146105ed578063af1e1de314610632578063b01ef60814610664578063b103d74f1461067757600080fd5b80639979ef45116100d15780639979ef45146104cd5780639e64ba6c146104e05780639e79b41f1461054a578063a59ac6dd146105da57600080fd5b80637de3bd07146104725780638129fc1c14610485578063895633ba1461049a57600080fd5b80634635256e1161016f578063614b151c1161013e578063614b151c1461040c5780636775d96a1461041f5780637430e0c614610432578063798bac8d1461045257600080fd5b80634635256e1461036d57806347e35740146103ac5780634ce6931a146103cc5780635d83d562146103ec57600080fd5b806321561935116101ab578063215619351461029f578063262907c5146102bf57806329e0e160146102fc5780632ab2b52b1461031c57600080fd5b806303ec16d71461023f57806306dcf7481461025f57806321506fff1461027f57600080fd5b3661023a57336001600160a01b037f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504431614610238576040517f37de3dd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561024b57600080fd5b5061023861025a366004614f62565b610723565b34801561026b57600080fd5b5061023861027a36600461500b565b61083a565b34801561028b57600080fd5b5061023861029a3660046150a5565b610c95565b3480156102ab57600080fd5b506102386102ba3660046150d3565b610eb0565b3480156102cb57600080fd5b506102df6102da3660046150d3565b611025565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561030857600080fd5b506102386103173660046150ff565b6110a6565b34801561032857600080fd5b5061035f6103373660046150d3565b6001600160a01b03909116600090815261177660209081526040808320938352929052205490565b6040519081526020016102f3565b34801561037957600080fd5b5061038d6103883660046150d3565b611243565b604080516001600160a01b0390931683526020830191909152016102f3565b3480156103b857600080fd5b5061035f6103c73660046150a5565b6112b9565b3480156103d857600080fd5b506102386103e7366004615147565b6112f1565b3480156103f857600080fd5b5061023861040736600461517c565b6114ef565b61035f61041a3660046151c8565b61180e565b61023861042d366004615228565b611ba8565b34801561043e57600080fd5b5061023861044d3660046150a5565b611bbf565b34801561045e57600080fd5b5061023861046d366004615147565b611c77565b61035f610480366004615147565b611e97565b34801561049157600080fd5b50610238611ea6565b3480156104a657600080fd5b507f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436102df565b6102386104db3660046150a5565b611f76565b3480156104ec57600080fd5b506102df6104fb3660046150a5565b60009081526117776020526040908190206006810154600290910154600160a01b9182900467ffffffffffffffff1691900490911b73ffffffffffffffffffffffff0000000000000000161790565b34801561055657600080fd5b5061056a6105653660046150a5565b611f82565b6040516102f391906000610100820190506001600160a01b0380845116835260208401516020840152806040850151166040840152606084015160608401526080840151608084015260a084015160a08401528060c08501511660c08401525060e083015160e083015292915050565b6102386105e8366004615147565b61207c565b3480156105f957600080fd5b5061060d6106083660046150d3565b61208e565b604080516001600160a01b0390941684526020840192909252908201526060016102f3565b34801561063e57600080fd5b5061065261064d366004615147565b612110565b6040516102f396959493929190615282565b6102386106723660046151c8565b61214e565b610238610685366004615330565b612224565b610238610698366004615395565b612731565b3480156106a957600080fd5b507f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d6102df565b3480156106dc57600080fd5b5061035f6106eb3660046150d3565b612b47565b3480156106fc57600080fd5b507f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb66102df565b808060000361074557604051631d4b87f360e11b815260040160405180910390fd5b60008381526117776020526040902060028101546001600160a01b03163314610797576002810154604051632600954360e21b81526001600160a01b0390911660048201526024015b60405180910390fd5b6005810154156107ba57604051635aea7c4760e01b815260040160405180910390fd5b828160070154036107f7576040517f4b669ac700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007810183905560405183815284907f0c0f2662914f0cd1e952db2aa425901cb00e7c1f507687d22cb04e836d55d9c7906020015b60405180910390a250505050565b604051630935e01b60e21b81523360048201527f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb66001600160a01b0316906324d7806c90602401602060405180830381865afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c291906153ce565b6108df5760405163af8db33360e01b815260040160405180910390fd5b6002610b8754036109325760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556000819003610973576040517ffedbcec600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8483146109ac576040517f4716476200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015610c8657600061233760008989858181106109d0576109d06153f0565b90506020020160208101906109e59190615406565b6001600160a01b03166001600160a01b031681526020019081526020016000206000878785818110610a1957610a196153f0565b602090810292909201358352508181019290925260409081016000908120825160a081018452815463ffffffff808216835264010000000082046001600160601b031696830196909652600160801b90046fffffffffffffffffffffffffffffffff1693810193909352600101546001600160a01b0381166060840152600160a01b90049092166080820152915061233790898985818110610abd57610abd6153f0565b9050602002016020810190610ad29190615406565b6001600160a01b03166001600160a01b031681526020019081526020016000206000878785818110610b0657610b066153f0565b602090810292909201358352508101919091526040016000908120908155600101805477ffffffffffffffffffffffffffffffffffffffffffffffff1916905580514263ffffffff90911610610c7d5760608101518151602083015160405163345db49360e01b81526001600160a01b03938416600482015263ffffffff90921660248301526001600160601b031660448201527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504439091169063345db49390606401600060405180830381600087803b158015610be257600080fd5b505af1158015610bf6573d6000803e3d6000fd5b50505050858583818110610c0c57610c0c6153f0565b90506020020135888884818110610c2557610c256153f0565b9050602002016020810190610c3a9190615406565b6001600160a01b03167fd3802baab3d80ff411a2f83c8394d07877339ff8352f7c1b02fbcd70ea7cac8c8686604051610c74929190615423565b60405180910390a35b506001016109af565b50506001610b87555050505050565b6002610b875403610ce85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b878190556000828152611777602090815260409182902082516101408101845281546001600160a01b03908116825260018301549382019390935293810154808316938501849052600160a01b908190046001600160601b0316606086015260038201546080860152600482015460a0860152600582015460c0860152600682015492831660e086015290910467ffffffffffffffff16610100840152600701546101208301523314610dc3576040808201519051632600954360e21b81526001600160a01b03909116600482015260240161078e565b60c081015115610de657604051635aea7c4760e01b815260040160405180910390fd5b80516001600160a01b03166000908152611776602090815260408083208285018051855290835281842084905585845261177790925280832080546001600160a01b031916815560018101849055600281018490556003810184905560048101849055600581018490556006810180546001600160e01b0319169055600701929092558251905191830151610e7b9290612bd4565b60405182907f14b9c40404d5b41deb481f9a40b8aeb2bf4b47679b38cf757075a66ed510f7f190600090a250506001610b8755565b6002610b875403610f035760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680610f67576040517fc09f8e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381163314610fb4576040517ff049b41a0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161078e565b6001600160a01b0383166000908152611f4e60209081526040808320858452909152812055610fe4838333612bd4565b60405182906001600160a01b038516907f70c7877531c04c7d9caa8a7eca127384f04e8a6ee58b63f778ce5401d8bcae4190600090a350506001610b875550565b6001600160a01b038216600090815261233760209081526040808320848452909152812080544263ffffffff90911610156110645760009150506110a0565b80546001820154600160801b90910460201b73ffffffffffffffffffffffffffffffff0000000016600160a01b90910463ffffffff16175b9150505b92915050565b6002610b8754036110f95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038416600090815261233760209081526040808320868452909152902080544263ffffffff90911610156111715780546040517f8c9e57cf00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260240161078e565b805464010000000090046001600160601b03168211156111d25780546040517f242373610000000000000000000000000000000000000000000000000000000081526401000000009091046001600160601b0316600482015260240161078e565b60018101546001600160a01b0384811691161461122c5760018101546040517fa7d95dc30000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161078e565b6112368585612bdf565b50506001610b8755505050565b6001600160a01b038083166000908152611f4e60209081526040808320858452909152812054909116908161127b57506000196112b2565b506001600160a01b0383166000908152611f4e60209081526040808320858452909152902054600160a01b90046001600160601b03165b9250929050565b600081815261177760205260408120600581015482036112dd576007015492915050565b6112ea8160070154612e7c565b9392505050565b6002610b8754036113445760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b875580600081900361136d57604051631d4b87f360e11b815260040160405180910390fd5b600061138161138d80546001810190915590565b905061138d8585612e9e565b6001600160a01b03851660009081526117766020908152604080832087845290915290205415611413576001600160a01b038516600090815261177660209081526040808320878452909152908190205490517f7618a003000000000000000000000000000000000000000000000000000000008152600481019190915260240161078e565b6001600160a01b038516600081815261177660209081526040808320888452825280832085905584835261177782529182902080546001600160a01b03199081168517825560018201899055600282018054339216821790556007820188905583517f00000000000000000000000000000000000000000000000000000000000151808152610384938101939093529282018790526060820185905292879290917f1062dd3b35f12b4064331244d00f40c1d4831965e4285654157a2409c6217cff9060800160405180910390a450506001610b875550505050565b604051630935e01b60e21b81523360048201527f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb66001600160a01b0316906324d7806c90602401602060405180830381865afa158015611553573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157791906153ce565b6115945760405163af8db33360e01b815260040160405180910390fd5b6002610b8754036115e75760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556000819003611628576040517ffbaca1c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526117776020908152604080832081516101408101835281546001600160a01b039081168252600183015494820194909452600282015480851693820193909352600160a01b928390046001600160601b0316606082015260038201546080820152600482015460a0820152600582015460c0820152600682015493841660e08201529190920467ffffffffffffffff16610100820152600790910154610120820181905290910361170a576040517fe3a2ab0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516001600160a01b03166000908152611776602090815260408083208285018051855290835281842084905587845261177790925280832080546001600160a01b031916815560018101849055600281018490556003810184905560048101849055600581018490556006810180546001600160e01b031916905560070192909255825190519183015161179f9290612bd4565b60e08101516001600160a01b0316156117c8576117c88160e00151826101200151614e20612eac565b837f1d56d378404d81e3fc5f3dfbf88359b8cb2ecafa73b3270c478bf7b2bdd1446984846040516117fa929190615423565b60405180910390a250506001610b87555050565b600061181b858585612fee565b1561182857506000611ba0565b611832858561305c565b15611869576040517f83a483f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516600090815261233760209081526040808320878452909152902080544263ffffffff9091161015611951576040517f4ec58ed7000000000000000000000000000000000000000000000000000000008152336004820152602481018590527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436001600160a01b031690634ec58ed790349060440160206040518083038185885af1158015611925573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061194a9190615452565b9150611a93565b80546000906119709064010000000090046001600160601b0316612e7c565b9050808510156119af576040517fe40a30e60000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b600182015482546040517f5fdec5610000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015263ffffffff821660248201526401000000009091046001600160601b03166044820152336064820152608481018790527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d5044390911690635fdec56190349060a40160206040518083038185885af1158015611a6a573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611a8f9190615452565b9250505b600181018054825463ffffffff8581167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909216919091176401000000006001600160601b03891602176fffffffffffffffffffffffffffffffff908116602088811c909216600160801b0217855577ffffffffffffffffffffffffffffffffffffffffffffffff19909216337fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff811691909117600160a01b92881692909202919091179092556040805187815291820185905287916001600160a01b038a16917ece0a712e4e277ac7b34942865f0de7a5629dffe0539b70423ad5ff1ed6ab42910160405180910390a4505b949350505050565b611bb786863487878787612224565b505050505050565b6002610b875403611c125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755600081815261177760205260408120600501549003611c63576040517f4b6ad8fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c6e8160006130a8565b506001610b8755565b6002610b875403611cca5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755611cdb8383836132e1565b611e8c576001600160601b03811115611d20576040517f35ec82cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038381166000908152611f4e60209081526040808320868452909152902080549091811690600160a01b90046001600160601b031683148015611d7257506001600160a01b03811615155b15611da9576040517fb6950f3600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546001600160601b038416600160a01b026001600160a01b039182161783558116611dee57611dd98585612e9e565b81546001600160a01b03191633178255611e3b565b6001600160a01b0381163314611e3b576040517f697d918e0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161078e565b336001600160a01b031684866001600160a01b03167ffcc77ea8bdcce862f43b7fb00fe6b0eb90d6aeead27d3800d9257cf7a05f9d9686604051611e8191815260200190565b60405180910390a450505b50506001610b875550565b6000611ba0848484600061180e565b600054610100900460ff16611ec15760005460ff1615611ec5565b303b155b611f375760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161078e565b600054610100900460ff16158015611f59576000805461ffff19166101011790555b611f61613346565b8015611f73576000805461ff00191690555b50565b611f7381346000612731565b611fe560405180610100016040528060006001600160a01b031681526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600081525090565b506000908152611777602090815260409182902082516101008101845281546001600160a01b03908116825260018301549382019390935260028201548316938101939093527f000000000000000000000000000000000000000000000000000000000001518060608401526103846080840152600581015460a0840152600681015490911660c08301526007015460e082015290565b612089838383600061214e565b505050565b6001600160a01b03821660009081526123376020908152604080832084845290915281208054829182914263ffffffff90911610156120d857600080600093509350935050612109565b600181015490546001600160a01b03909116935063ffffffff8116925064010000000090046001600160601b031690505b9250925092565b60008060608060008060006121258a8a6133cb565b90506121358a8a838b60006133d7565b50949f919e50929c50909a509198509650945050505050565b6001600160a01b0384166000908152611f4e6020908152604080832086845290915290208054600160a01b90046001600160601b03168310156121d15780546040517f16b5016f000000000000000000000000000000000000000000000000000000008152600160a01b9091046001600160601b0316600482015260240161078e565b80546001600160a01b0316612212576040517fda48e18400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61221d858584613585565b5050505050565b6002610b8754036122775760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755428410156122b7576040517f98dbee1b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b426202a300018411156122f6576040517f3807938200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61230087876137bd565b3485111561238e5760405163452f2b8f60e01b815233600482015234860360248201527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436001600160a01b03169063452f2b8f90604401600060405180830381600087803b15801561237157600080fd5b505af1158015612385573d6000803e3d6000fd5b505050506123c8565b348510156123c8576040517ff8c4782000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516331a9108f60e11b8152600481018790526000906001600160a01b03891690636352211e90602401602060405180830381865afa158015612410573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612434919061546b565b6001600160a01b03808a166000908152611b66602090815260408083208c84528252808320338452825280832093851683529281528282208a835281528282208983529052205490915060ff16156124b8576040517fd0be1ad300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038881166000818152611b66602090815260408083208c845282528083203380855290835281842095871684529482528083208b845282528083208a84528252808320805460ff1916600117905580517f6f5a5497fcb7364f6bad56db9aad5785b6786717424e748b8bfef6e6554cd5518184015280820194909452606084018c9052608084019490945260a083018a905260c08084018a90528451808503909101815260e0840190945283519301929092207f19010000000000000000000000000000000000000000000000000000000000006101008301527fa4c523e6efc3db1cacdbf55085f995f1433e482eb9b0634e8c094e0ef60eb5326101028301526101228201526101420160408051601f19818403018152919052805160209091012090506001600160a01b0382166125fa828787876137fb565b6001600160a01b03161461263a576040517f3902771600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506040516323b872dd60e01b81526001600160a01b038281166004830152336024830152604482018990528916906323b872dd90606401600060405180830381600087803b15801561268b57600080fd5b505af115801561269f573d6000803e3d6000fd5b5050505060008060006126b68b8b868c6000613823565b604080513381526020810185905290810183905260608101829052608081018c905292955090935091506001600160a01b03808616918c918e16907f6c623fa5e13aaaf28288f807e5b4f9ec6fb7ef812568e00317c552663bea918f9060a00160405180910390a450506001610b8755505050505050505050565b6002610b8754036127845760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b875560008381526117776020526040812060078101549091036127d7576040517f125197d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34831015612811576040517fe2bbc1e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058101546001600160a01b03831615158061282c57508015155b1561289a576002820180546001600160a01b0316604085901c6001600160601b0316600160a01b908102919091179091556006830180547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1667ffffffffffffffff86169092029190911790555b8060000361294d5781600701548410156128e85781600701546040517f31e6f71c00000000000000000000000000000000000000000000000000000000815260040161078e91815260200190565b81546001830154612902916001600160a01b031690613a82565b50600781018390556006810180546001600160a01b03191633179055427f00000000000000000000000000000000000000000000000000000000000151800160058201819055612a73565b4281101561298a576040517f3feeb88d0000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b6006820154336001600160a01b03909116036129d2576040517fe140576800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006129e18360070154612e7c565b905080851015612a20576040517fcd698a190000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b50600782018054600684018054928790556001600160a01b0319831633179055906001600160a01b0316426103840180841015612a6257600585018190559250825b50612a708183614e20612eac565b50505b34841115612afd5760405163452f2b8f60e01b815233600482015234850360248201527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436001600160a01b03169063452f2b8f90604401600060405180830381600087803b158015612ae457600080fd5b505af1158015612af8573d6000803e3d6000fd5b505050505b6040805185815260208101839052339187917f26ea3ebbda62eb1baef13e1c237dddd956c87f80b2801f2616d806d52557b121910160405180910390a350506001610b8755505050565b6001600160a01b038216600090815261233760209081526040808320848452909152812080544263ffffffff90911610612b9f578054612b979064010000000090046001600160601b0316612e7c565b9150506110a0565b5060019392505050565b6001600160a01b03163b151590565b6000612bc383613a8c565b80156112ea57506112ea8383613abf565b612089838383613bbd565b6001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046001600160601b03908116848901908152600160801b9093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c52979099529890945577ffffffffffffffffffffffffffffffffffffffffffffffff1990961690965591518251955193517f4dc8fb3c000000000000000000000000000000000000000000000000000000008152908716600482015294909316602485015291166044830152917f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504431690634dc8fb3c90606401600060405180830381600087803b158015612d2357600080fd5b505af1158015612d37573d6000803e3d6000fd5b5050505060608101516040516323b872dd60e01b81523360048201526001600160a01b03918216602482015260448101849052908416906323b872dd90606401600060405180830381600087803b158015612d9157600080fd5b505af1925050508015612da2575060015b612db657612db68383836060015133613bf4565b6000806000612e0c86863387602001516001600160601b0316612e0789604001518a6080015173ffffffffffffffffffffffffffffffff0000000060209290921b9190911663ffffffff9091161790565b613823565b606080880151604080513381526020810187905290810185905291820183905293965091945092506001600160a01b03918216918791908916907f1cb8adb37d6d35e94cd0695ca39895b84371864713f5ca7eada52af9ff23744b906080015b60405180910390a4505050505050565b6000600a8204808203612e94576112ea83600161549e565b6112ea838261549e565b612ea88282613c00565b5050565b81600003612eb957505050565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d8060008114612f09576040519150601f19603f3d011682016040523d82523d6000602084013e612f0e565b606091505b5050905080612fe8576040517faa67c9190000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063aa67c9199085906024016000604051808303818588803b158015612f9457600080fd5b505af1158015612fa8573d6000803e3d6000fd5b5050505050836001600160a01b03167fa2201512569adb2d513531dfd69b66df50bd5cffb8c1bbe65a4611f9e1eadbd18460405161082c91815260200190565b50505050565b6001600160a01b038084166000908152611f4e60209081526040808320868452909152812080549192909116158061303657508054600160a01b90046001600160601b031683105b156130455760009150506112ea565b61305185856000613585565b506001949350505050565b6001600160a01b038216600090815261177660209081526040808320848452909152812054801580159061109c5750600090815261177760205260409020600501544211159392505050565b6000828152611777602090815260409182902082516101408101845281546001600160a01b039081168252600183015493820193909352600282015480841694820194909452600160a01b938490046001600160601b0316606082015260038201546080820152600482015460a0820152600582015460c08201819052600683015493841660e08301529390920467ffffffffffffffff1661010083015260070154610120820152904211613191578060c001516040517f3a017f6000000000000000000000000000000000000000000000000000000000815260040161078e91815260200190565b80516001600160a01b03166000908152611776602090815260408083208285015184528252808320839055858352611777909152812080546001600160a01b031916815560018101829055600281018290556003810182905560048101829055600581018290556006810180546001600160e01b0319169055600701558161322c5761322c816000015182602001518360e001516000613c6a565b600080600061327a84600001518560200151866040015187610120015188610100015167ffffffffffffffff1660408a606001516001600160601b03166001600160a01b0316901b17613823565b9250925092508360e001516001600160a01b031684604001516001600160a01b0316877f2edb0e99c6ac35be6731dab554c1d1fa1b7beb675090dbb09fb14e615aca1c4a868686604051612e6c939291909283526020830191909152604082015260600190565b6001600160a01b038316600090815261233760209081526040808320858452909152812080544263ffffffff909116108061332d5750805464010000000090046001600160601b031683115b1561333c5760009150506112ea565b6130518585612bdf565b600054610100900460ff166133c35760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161078e565b600161138d55565b60006112ea8383613d19565b600060608060008060008060008c6001600160a01b03166340c1a064619c408e6040518363ffffffff1660e01b815260040161341591815260200190565b6020604051808303818786fa9350505050801561344f575060408051601f3d908101601f1916820190925261344c9181019061546b565b60015b156134575790505b6134618d8d613d4f565b8151919850965060148b0490156134fd57816001600160a01b03168c6001600160a01b031614806134c95750875160011480156134c95750876000815181106134ac576134ac6153f0565b60200260200101516001600160a01b03168c6001600160a01b0316145b156134d857808b039550613506565b600a8b0495508b9450856134ec828d6154b6565b6134f691906154b6565b9350613506565b8b9450808b0393505b6001600160a01b038a161580159061352757506001600160a01b038a163314155b801561354557508b6001600160a01b03168a6001600160a01b031614155b80156135635750816001600160a01b03168a6001600160a01b031614155b1561356f576005810492505b82810398505050959b949a509550955095509550565b6002610b8754036135d85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038381166000908152611f4e60209081526040808320868452808352818420825180840190935280549586168352600160a01b9095046001600160601b031682840152868452909152915561363a84846137bd565b3481602001516001600160601b031611156136eb57602081015160405163452f2b8f60e01b8152336004820152346001600160601b039092169190910360248201819052906001600160a01b037f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063452f2b8f90604401600060405180830381600087803b1580156136cd57600080fd5b505af11580156136e1573d6000803e3d6000fd5b505050505061371b565b3481602001516001600160601b0316101561371b57602081015161371b9033906001600160601b0316340361442e565b6137288484336000613bf4565b600080600061374b8787866000015187602001516001600160601b031689613823565b865160408051338152602081018690529081018490526060810183905293965091945092506001600160a01b039081169188918a16907fd28c0a7dd63bc853a4e36306655da9f8c0b29ff9d0605bb976ae420e46a999309060800160405180910390a450506001610b87555050505050565b6001600160a01b0380831660009081526123376020908152604080832085845290915290206001810154909133911603612089576120898383614547565b600080600061380c878787876146f8565b91509150613819816147e5565b5095945050505050565b6000806000606080600080600061383d8d8d8d8d8d6133d7565b8681019e50929c509a509298509096509094509250905081156138c9576138678983614e2061499b565b156138c457604080516001600160a01b038b81168252602082018590526000928201929092528d918f16907f141b92fd9766c80ab120598ea2f6be9802470ec59b5446dd9bf46214ead8d08e9060600160405180910390a36138c9565b600091015b6138f67f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb682614e20612eac565b8615613a655784516001811115613a3b576005811115613914575060055b6000805b8281101561397557612710878281518110613935576139356153f0565b6020026020010151111561394c5760019250613975565b86818151811061395e5761395e6153f0565b602002602001015182019150806001019050613918565b508060000361398357600191505b600060015b83811015613a00576000838983815181106139a5576139a56153f0565b60200260200101518d6139b891906154cd565b6139c291906154ec565b90506139ce818461549e565b92506139f78a83815181106139e5576139e56153f0565b60200260200101518262033450612eac565b50600101613988565b50613a3488600081518110613a1757613a176153f0565b6020026020010151828c613a2b91906154b6565b62033450612eac565b5050613a63565b613a6386600081518110613a5157613a516153f0565b60200260200101518962033450612eac565b505b613a728387614e20612eac565b5050505050955095509592505050565b612ea88282614a09565b6000613a9f826301ffc9a760e01b613abf565b80156110a05750613ab8826001600160e01b0319613abf565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090613b3b90869061550e565b6000604051808303818686fa925050503d8060008114613b77576040519150601f19603f3d011682016040523d82523d6000602084013e613b7c565b606091505b5091509150602081511015613b9757600093505050506110a0565b818015613bb3575080806020019051810190613bb391906153ce565b9695505050505050565b6001600160a01b038084166000908152611f4e602090815260408083208684529091529020541680612fe857612fe8848484614a1d565b612fe884848484614a53565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680613c36576120898383614ad7565b6001600160a01b0381163314612089576040516332f3b03360e01b81526001600160a01b038216600482015260240161078e565b6001600160a01b03811615613cab576040517f57a016b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516323b872dd60e01b81523060048201526001600160a01b038381166024830152604482018590528516906323b872dd90606401600060405180830381600087803b158015613cfb57600080fd5b505af1158015613d0f573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038083166000908152611f4e6020908152604080832085845290915290205416806110a0576112ea8383614bb5565b606080613d6c6001600160a01b03851663152a902d60e11b613abf565b15613e475760405163152a902d60e11b81526004810184905261271060248201526001600160a01b03851690632a55205a90619c409060440160408051808303818786fa93505050508015613dde575060408051601f3d908101601f19168201909252613ddb91810190615549565b60015b15613e47578015613e445760408051600180825281830190925290602080830190803683370190505093508184600081518110613e1d57613e1d6153f0565b60200260200101906001600160a01b031690816001600160a01b03168152505050506112b2565b50505b613e616001600160a01b038516635d9dd7eb60e11b613abf565b15613efb57604051635d9dd7eb60e11b8152600481018490526001600160a01b0385169063bb3bafd690619c40906024016000604051808303818786fa93505050508015613ed157506040513d6000823e601f3d908101601f19168201604052613ece91908101906156b1565b60015b15613efb5781518015801590613ee75750815181145b15613ef7575090925090506112b2565b5050505b6040517fde5488af0000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d169063de5488af90619c40906024016020604051808303818786fa93505050508015613f9b575060408051601f3d908101601f19168201909252613f989181019061546b565b60015b1561416b57846001600160a01b0316816001600160a01b03161461416957935083613fd66001600160a01b03821663152a902d60e11b613abf565b156140a95760405163152a902d60e11b81526004810185905261271060248201526001600160a01b03861690632a55205a90619c409060440160408051808303818786fa93505050508015614048575060408051601f3d908101601f1916820190925261404591810190615549565b60015b156140a95760408051600180825281830190925290602080830190803683370190505094508185600081518110614081576140816153f0565b60200260200101906001600160a01b031690816001600160a01b0316815250505050506112b2565b82511580156140cd57506140cd6001600160a01b038616635d9dd7eb60e11b613abf565b1561416957604051635d9dd7eb60e11b8152600481018590526001600160a01b0386169063bb3bafd690619c40906024016000604051808303818786fa9350505050801561413d57506040513d6000823e601f3d908101601f1916820160405261413a91908101906156b1565b60015b1561416957815180158015906141535750815181145b15614165575090935091506112b29050565b5050505b505b61419e6001600160a01b0385167fb779958400000000000000000000000000000000000000000000000000000000613abf565b156142d7576040517fb9c4d9fb000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0385169063b9c4d9fb90619c40906024016000604051808303818786fa9350505050801561422757506040513d6000823e601f3d908101601f191682016040526142249190810190615715565b60015b156142d757805180156142d4576040517f0ebd4c7f000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03871690630ebd4c7f90619c40906024016000604051808303818786fa935050505080156142b857506040513d6000823e601f3d908101601f191682016040526142b5919081019061574a565b60015b156142d457805182036142d2579193509091506112b29050565b505b50505b6040517f40c1a064000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b038516906340c1a06490619c40906024016020604051808303818786fa93505050508015614356575060408051601f3d908101601f191682019092526143539181019061546b565b60015b156143c45760015b604051908082528060200260200182016040528015614387578160200160208202803683370190505b509250808360008151811061439e5761439e6153f0565b60200260200101906001600160a01b031690816001600160a01b031681525050506112b2565b836001600160a01b0316638da5cb5b619c406040518263ffffffff1660e01b81526004016020604051808303818786fa93505050508015614422575060408051601f3d908101601f1916820190925261441f9181019061546b565b60015b156112b257600161435e565b8047101561447e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161078e565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146144cb576040519150601f19603f3d011682016040523d82523d6000602084013e6144d0565b606091505b50509050806120895760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161078e565b6001600160a01b0382166000908152612337602090815260408083208484529091529020544263ffffffff90911610612ea8576001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046001600160601b03908116848901908152600160801b9093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c52979099529890945577ffffffffffffffffffffffffffffffffffffffffffffffff19909616909655915182519551935163345db49360e01b8152908716600482015294909316602485015291166044830152917f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063345db49390606401600060405180830381600087803b1580156146a557600080fd5b505af11580156146b9573d6000803e3d6000fd5b50506040518492506001600160a01b03861691507f30c264456cbd17f5f67d7534654161414f34c0e6cc1b7500e169b7a7aea4afc090600090a3505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561472f57506000905060036147dc565b8460ff16601b1415801561474757508460ff16601c14155b1561475857506000905060046147dc565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156147ac573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166147d5576000600192509250506147dc565b9150600090505b94509492505050565b60008160048111156147f9576147f961577f565b036148015750565b60018160048111156148155761481561577f565b036148625760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161078e565b60028160048111156148765761487661577f565b036148c35760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161078e565b60038160048111156148d7576148d761577f565b0361492f5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161078e565b60048160048111156149435761494361577f565b03611f735760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161078e565b6000826000036149ad575060006112ea565b6040516001600160a01b03851690839085906000818181858888f193505050503d80600081146149f9576040519150601f19603f3d011682016040523d82523d6000602084013e6149fe565b606091505b509095945050505050565b614a138282614547565b612ea88282614bf9565b6001600160a01b038316600090815261177660209081526040808320858452909152812054900361208957612089838383614c32565b6001600160a01b038085166000908152611f4e60209081526040808320878452909152902054168015614acb57816001600160a01b0316816001600160a01b031614614abd576040516332f3b03360e01b81526001600160a01b038216600482015260240161078e565b60009150614acb8585614c9f565b61221d85858585614cf2565b6001600160a01b03821660009081526117766020908152604080832084845290915281205490819003614b0e576120898383614e94565b6000818152611777602052604081206005810154909103614b6c5760028101546001600160a01b03163314614b67576002810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b612fe8565b60068101546001600160a01b03163314614baa576006810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b612fe88260016130a8565b6001600160a01b03808316600090815261177660209081526040808320858452825280832054835261177790915290206002015416806110a0576112ea8383614ef6565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020805490911615612089576120898383614c9f565b6040516323b872dd60e01b81523060048201526001600160a01b038281166024830152604482018490528416906323b872dd90606401600060405180830381600087803b158015614c8257600080fd5b505af1158015614c96573d6000803e3d6000fd5b50505050505050565b6001600160a01b0382166000818152611f4e60209081526040808320858452909152808220829055518392917faa6271d89a385571e237d3e7254ccc7c09f68055e6e9b410ed08233a8b9a05cf91a35050565b6001600160a01b0384166000908152611776602090815260408083208684529091529020548015614e88576000818152611777602052604081206005810154909103614e36576001600160a01b03831615801590614d60575060028101546001600160a01b03848116911614155b15614d8f576002810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b6001600160a01b038616600090815261177660209081526040808320888452825280832083905584835261177790915280822080546001600160a01b031916815560018101839055600281018390556003810183905560048101839055600581018390556006810180546001600160e01b03191690556007018290555183917f5603897cc9b1e866f3f7395ffc6638776041f21c094d0b4e748ff44c407fa36291a2614e82565b60068101546001600160a01b03848116911614614e77576006810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b614e828260016130a8565b60009250505b61221d85858585613c6a565b6040516323b872dd60e01b8152336004820152306024820152604481018290526001600160a01b038316906323b872dd90606401600060405180830381600087803b158015614ee257600080fd5b505af1158015611bb7573d6000803e3d6000fd5b6040516331a9108f60e11b8152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa158015614f3e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ea919061546b565b60008060408385031215614f7557600080fd5b50508035926020909101359150565b60008083601f840112614f9657600080fd5b50813567ffffffffffffffff811115614fae57600080fd5b6020830191508360208260051b85010111156112b257600080fd5b60008083601f840112614fdb57600080fd5b50813567ffffffffffffffff811115614ff357600080fd5b6020830191508360208285010111156112b257600080fd5b6000806000806000806060878903121561502457600080fd5b863567ffffffffffffffff8082111561503c57600080fd5b6150488a838b01614f84565b9098509650602089013591508082111561506157600080fd5b61506d8a838b01614f84565b9096509450604089013591508082111561508657600080fd5b5061509389828a01614fc9565b979a9699509497509295939492505050565b6000602082840312156150b757600080fd5b5035919050565b6001600160a01b0381168114611f7357600080fd5b600080604083850312156150e657600080fd5b82356150f1816150be565b946020939093013593505050565b6000806000806080858703121561511557600080fd5b8435615120816150be565b9350602085013592506040850135615137816150be565b9396929550929360600135925050565b60008060006060848603121561515c57600080fd5b8335615167816150be565b95602085013595506040909401359392505050565b60008060006040848603121561519157600080fd5b83359250602084013567ffffffffffffffff8111156151af57600080fd5b6151bb86828701614fc9565b9497909650939450505050565b600080600080608085870312156151de57600080fd5b84356151e9816150be565b935060208501359250604085013591506060850135615207816150be565b939692955090935050565b803560ff8116811461522357600080fd5b919050565b60008060008060008060c0878903121561524157600080fd5b863561524c816150be565b9550602087013594506040870135935061526860608801615212565b92506080870135915060a087013590509295509295509295565b600060c082018883526020888185015260c0604085015281885180845260e086019150828a01935060005b818110156152d25784516001600160a01b0316835293830193918301916001016152ad565b50508481036060860152875180825290820192508188019060005b81811015615309578251855293830193918301916001016152ed565b50505050608083018590526001600160a01b03841660a08401529050979650505050505050565b600080600080600080600060e0888a03121561534b57600080fd5b8735615356816150be565b965060208801359550604088013594506060880135935061537960808901615212565b925060a0880135915060c0880135905092959891949750929550565b6000806000606084860312156153aa57600080fd5b833592506020840135915060408401356153c3816150be565b809150509250925092565b6000602082840312156153e057600080fd5b815180151581146112ea57600080fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561541857600080fd5b81356112ea816150be565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60006020828403121561546457600080fd5b5051919050565b60006020828403121561547d57600080fd5b81516112ea816150be565b634e487b7160e01b600052601160045260246000fd5b600082198211156154b1576154b1615488565b500190565b6000828210156154c8576154c8615488565b500390565b60008160001904831182151516156154e7576154e7615488565b500290565b60008261550957634e487b7160e01b600052601260045260246000fd5b500490565b6000825160005b8181101561552f5760208186018101518583015201615515565b8181111561553e576000828501525b509190910192915050565b6000806040838503121561555c57600080fd5b8251615567816150be565b6020939093015192949293505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156155b6576155b6615577565b604052919050565b600067ffffffffffffffff8211156155d8576155d8615577565b5060051b60200190565b600082601f8301126155f357600080fd5b81516020615608615603836155be565b61558d565b82815260059290921b8401810191818101908684111561562757600080fd5b8286015b8481101561564b57805161563e816150be565b835291830191830161562b565b509695505050505050565b600082601f83011261566757600080fd5b81516020615677615603836155be565b82815260059290921b8401810191818101908684111561569657600080fd5b8286015b8481101561564b578051835291830191830161569a565b600080604083850312156156c457600080fd5b825167ffffffffffffffff808211156156dc57600080fd5b6156e8868387016155e2565b935060208501519150808211156156fe57600080fd5b5061570b85828601615656565b9150509250929050565b60006020828403121561572757600080fd5b815167ffffffffffffffff81111561573e57600080fd5b61109c848285016155e2565b60006020828403121561575c57600080fd5b815167ffffffffffffffff81111561577357600080fd5b61109c84828501615656565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220042140fd23aa099c914a95e26659cefe3bfa4304f57fce37f994ce9903b4919264736f6c634300080d0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb600000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d0000000000000000000000000000000000000000000000000000000000015180000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f

-----Decoded View---------------
Arg [0] : treasury (address): 0x67Df244584b67E8C51B10aD610aAfFa9a402FdB6
Arg [1] : feth (address): 0x49128CF8ABE9071ee24540a296b5DED3F9D50443
Arg [2] : royaltyRegistry (address): 0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D
Arg [3] : duration (uint256): 86400
Arg [4] : marketProxyAddress (address): 0xcDA72070E455bb31C7690a170224Ce43623d0B6f

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb6
Arg [1] : 00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443
Arg [2] : 000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d
Arg [3] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [4] : 000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f


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
[ 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.