ETH Price: $2,728.95 (+0.35%)

Contract

0x4BDBe85eFE65b89F6128163D69F02da584017dDF
 

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

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block
From
To
186927572023-12-01 16:28:59447 days ago1701448139  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
NFTMarketRouter

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 1337000 runs

Other Settings:
shanghai EvmVersion
File 1 of 50 : NFTMarketRouter.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.18;

import "./mixins/shared/TxDeadline.sol";
import "./mixins/shared/WorldsNftNode.sol";
import "./mixins/shared/NFTMarketNode.sol";
import "./mixins/shared/NFTDropMarketNode.sol";
import "./mixins/shared/NFTCollectionFactoryNode.sol";

import "./mixins/nftMarketRouter/NFTCreateAndListTimedEditionCollection.sol";
import "./mixins/nftMarketRouter/NFTCreateAndListLimitedEditionCollection.sol";
import { NFTMarketRouterCore } from "./mixins/nftMarketRouter/NFTMarketRouterCore.sol";
import { NFTMarketRouterCreateFixedPriceSale } from "./mixins/nftMarketRouter/NFTMarketRouterCreateFixedPriceSale.sol";
import { NFTMarketRouterList } from "./mixins/nftMarketRouter/NFTMarketRouterList.sol";
import { NFTMarketRouterWorldsNftUserRoles } from "./mixins/nftMarketRouter/NFTMarketRouterWorldsNftUserRoles.sol";
import { NFTMarketRouterDutchAuction } from "./mixins/nftMarketRouter/NFTMarketRouterDutchAuction.sol";

import "./mixins/nftMarketRouter/apis/NFTMarketRouterAPIs.sol";
import "./mixins/nftMarketRouter/apis/NFTDropMarketRouterAPIs.sol";
import "./mixins/nftMarketRouter/apis/NFTCollectionFactoryRouterAPIs.sol";
import "./mixins/nftMarketRouter/apis/WorldsNftRouterAPIs.sol";

/**
 * @title A contract which offers value-added APIs and routes requests to the NFTMarket's existing API.
 * @dev Features in this contract can be created with a clear separation of concerns from the NFTMarket contract.
 * It also provides the contract size space required for targeted APIs and to experiment with new features.
 * @author batu-inal & HardlyDifficult & reggieag
 */
contract NFTMarketRouter is
  TxDeadline,
  NFTMarketNode,
  NFTCollectionFactoryNode,
  NFTDropMarketNode,
  NFTMarketRouterCore,
  WorldsNftNode,
  NFTMarketRouterAPIs,
  NFTCollectionFactoryRouterAPIs,
  NFTDropMarketRouterAPIs,
  WorldsNftRouterAPIs,
  NFTMarketRouterList,
  NFTCreateAndListTimedEditionCollection,
  NFTCreateAndListLimitedEditionCollection,
  NFTMarketRouterWorldsNftUserRoles,
  NFTMarketRouterCreateFixedPriceSale,
  NFTMarketRouterDutchAuction
{
  /**
   * @notice Initialize the template's immutable variables.
   * @param _nftMarket The address of the NFTMarket contract to which requests will be routed.
   * @param _nftDropMarket The address of the NFTDropMarket contract to which requests will be routed.
   * @param _nftCollectionFactory The address of the NFTCollectionFactory contract to which requests will be routed.
   * @param _worlds The address of the Worlds contract to which requests will be routed.
   */
  constructor(
    address _nftMarket,
    address _nftDropMarket,
    address _nftCollectionFactory,
    address _worlds
  )
    NFTMarketNode(_nftMarket)
    NFTDropMarketNode(_nftDropMarket)
    NFTCollectionFactoryNode(_nftCollectionFactory)
    WorldsNftNode(_worlds)
  {}
}

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

pragma solidity ^0.8.2;

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

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

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

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

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

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library 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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

File 4 of 50 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

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

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

File 6 of 50 : 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 7 of 50 : IFethMarket.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

/**
 * @notice Interface for functions the market uses in FETH.
 * @author batu-inal & HardlyDifficult
 */
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 lockupFor,
    uint256 lockupAmount
  ) external payable returns (uint256 expiration);
}

File 8 of 50 : IMarketUtils.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import "../../mixins/shared/MarketStructs.sol";

interface IMarketUtils {
  function getTransactionBreakdown(
    MarketTransactionOptions calldata options
  )
    external
    view
    returns (
      uint256 protocolFeeAmount,
      address payable[] memory creatorRecipients,
      uint256[] memory creatorShares,
      uint256 sellerRev,
      uint256 buyReferrerFee,
      uint256 sellerReferrerFee
    );
}

File 9 of 50 : INFTCollectionType.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

/**
 * @title Declares the type of the collection contract.
 * @dev This interface is declared as an ERC-165 interface.
 * @author reggieag
 */
interface INFTCollectionType {
  function getNFTCollectionType() external view returns (string memory collectionType);
}

File 10 of 50 : INFTMarketGetters.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import "../../mixins/shared/MarketStructs.sol";

/**
 * @title Interface with NFTMarket getters which are used by the router.
 * @author HardlyDifficult
 */
interface INFTMarketGetters {
  function getBuyPrice(address nftContract, uint256 tokenId) external view returns (address seller, uint256 price);

  function getExhibitionIdForNft(
    address nftContract,
    uint256 tokenId
  ) external view returns (uint256 worldOrExhibitionId);

  function getSaleStartsAt(address nftContract, uint256 tokenId) external view returns (uint256 saleStartsAt);

  function getReserveAuction(uint256 auctionId) external view returns (ReserveAuction memory auction);

  function getReserveAuctionIdFor(address nftContract, uint256 tokenId) external view returns (uint256 auctionId);
}

File 11 of 50 : IAdminRole.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

File 12 of 50 : IOperatorRole.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

File 13 of 50 : INFTCollectionFactoryDrops.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import "../../../libraries/AddressLibrary.sol";

/**
 * @title Interface for routing calls to the NFT Collection Factory to create drop collections.
 * @author reggieag
 */
interface INFTCollectionFactoryDrops {
  function createNFTDropCollection(
    string calldata name,
    string calldata symbol,
    string calldata baseURI,
    bool isRevealed,
    uint32 maxTokenId,
    address approvedMinter,
    uint96 nonce
  ) external returns (address collection);

  function createNFTDropCollectionWithPaymentFactory(
    string calldata name,
    string calldata symbol,
    string calldata baseURI,
    bool isRevealed,
    uint32 maxTokenId,
    address approvedMinter,
    uint96 nonce,
    CallWithoutValue calldata paymentAddressFactoryCall
  ) external returns (address collection);
}

File 14 of 50 : INFTCollectionFactoryLimitedEditions.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import "../../../libraries/AddressLibrary.sol";

/**
 * @title Interface for routing calls to the NFT Collection Factory to create limited edition collections.
 * @author gosseti
 */
interface INFTCollectionFactoryLimitedEditions {
  function createNFTLimitedEditionCollection(
    string calldata name,
    string calldata symbol,
    string calldata baseURI,
    uint32 maxTokenId,
    address approvedMinter,
    uint96 nonce
  ) external returns (address collection);

  function createNFTLimitedEditionCollectionWithPaymentFactory(
    string calldata name,
    string calldata symbol,
    string calldata baseURI,
    uint32 maxTokenId,
    address approvedMinter,
    uint96 nonce,
    CallWithoutValue calldata paymentAddressFactoryCall
  ) external returns (address collection);
}

File 15 of 50 : INFTCollectionFactoryTimedEditions.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import "../../../libraries/AddressLibrary.sol";

/**
 * @title Interface for routing calls to the NFT Collection Factory to create timed edition collections.
 * @author HardlyDifficult
 */
interface INFTCollectionFactoryTimedEditions {
  function createNFTTimedEditionCollection(
    string calldata name,
    string calldata symbol,
    string calldata tokenURI,
    uint256 mintEndTime,
    address approvedMinter,
    uint96 nonce
  ) external returns (address collection);

  function createNFTTimedEditionCollectionWithPaymentFactory(
    string calldata name,
    string calldata symbol,
    string calldata tokenURI,
    uint256 mintEndTime,
    address approvedMinter,
    uint96 nonce,
    CallWithoutValue calldata paymentAddressFactoryCall
  ) external returns (address collection);
}

File 16 of 50 : INFTDropMarketDutchAuction.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

interface INFTDropMarketDutchAuction {
  function createLinearDutchAuction(
    address nftContract,
    uint256 worldOrExhibitionId,
    uint256 maxPrice,
    uint256 minPrice,
    uint256 limitPerAccount,
    uint256 startTime,
    uint256 saleDuration
  ) external;
}

File 17 of 50 : INFTDropMarketFixedPriceSale.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

/**
 * @title Interface for routing calls to the NFT Drop Market to create fixed price sales.
 * @author HardlyDifficult & reggieag
 */
interface INFTDropMarketFixedPriceSale {
  function createFixedPriceSaleV3(
    address nftContract,
    uint256 worldOrExhibitionId,
    uint256 price,
    uint256 limitPerAccount,
    uint256 generalAvailabilityStartTime,
    uint256 txDeadlineTime
  ) external;

  function createFixedPriceSaleWithEarlyAccessAllowlistV2(
    address nftContract,
    uint256 worldOrExhibitionId,
    uint256 price,
    uint256 limitPerAccount,
    uint256 generalAvailabilityStartTime,
    uint256 earlyAccessStartTime,
    bytes32 merkleRoot,
    string calldata merkleTreeUri,
    uint256 txDeadlineTime
  ) external;
}

File 18 of 50 : INFTMarketBuyNow.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

/**
 * @title Interface for routing calls to the NFT Market to set buy now prices.
 * @author HardlyDifficult
 */
interface INFTMarketBuyNow {
  function cancelBuyPrice(address nftContract, uint256 tokenId) external;

  function setBuyPriceV2(address nftContract, uint256 tokenId, uint256 worldOrExhibitionId, uint256 price) external;
}

File 19 of 50 : INFTMarketExhibition.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

/**
 * @title Interface for routing calls to the NFT Market Exhibition
 * @author philbirt
 */
interface INFTMarketExhibitionForRouter {
  function updateExhibitionNft(address nftContract, uint256 tokenId, uint256 price) external;
}

File 20 of 50 : INFTMarketReserveAuction.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

/**
 * @title Interface for routing calls to the NFT Market to create reserve auctions.
 * @author HardlyDifficult & reggieag
 */
interface INFTMarketReserveAuction {
  function cancelReserveAuction(uint256 auctionId) external;

  function createReserveAuctionV3(
    address nftContract,
    uint256 tokenId,
    uint256 worldOrExhibitionId,
    uint256 reservePrice,
    uint256 duration
  ) external returns (uint256 auctionId);

  function updateReserveAuctionV2(uint256 auctionId, uint256 worldOrExhibitionId, uint256 reservePrice) external;
}

File 21 of 50 : INFTMarketScheduling.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

interface INFTMarketScheduling {
  function setSaleStartsAt(address nftContract, uint256 tokenId, uint256 saleStartsAt) external;
}

File 22 of 50 : IWorldsNftUserRoles.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

interface IWorldsNftUserRoles {
  function revokeAllRolesForUser(uint256 worldId, address user) external;

  function setAdminRole(uint256 worldId, address user) external;

  function setEditorRole(uint256 worldId, address user) external;
}

File 23 of 50 : AddressLibrary.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

struct CallWithoutValue {
  address target;
  bytes callData;
}

error AddressLibrary_Proxy_Call_Did_Not_Return_A_Contract(address addressReturned);

/**
 * @title A library for address helpers not already covered by the OZ library.
 * @author batu-inal & HardlyDifficult
 */
library AddressLibrary {
  using AddressUpgradeable for address;
  using AddressUpgradeable for address payable;

  /**
   * @notice Calls an external contract with arbitrary data and parse the return value into an address.
   * @param externalContract The address of the contract to call.
   * @param callData The data to send to the contract.
   * @return contractAddress The address of the contract returned by the call.
   */
  function callAndReturnContractAddress(
    address externalContract,
    bytes calldata callData
  ) internal returns (address payable contractAddress) {
    bytes memory returnData = externalContract.functionCall(callData);
    contractAddress = abi.decode(returnData, (address));
    if (!contractAddress.isContract()) {
      revert AddressLibrary_Proxy_Call_Did_Not_Return_A_Contract(contractAddress);
    }
  }

  function callAndReturnContractAddress(
    CallWithoutValue calldata call
  ) internal returns (address payable contractAddress) {
    contractAddress = callAndReturnContractAddress(call.target, call.callData);
  }
}

File 24 of 50 : RouteCallLibrary.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

error RouteCallLibrary_Call_Failed_Without_Revert_Reason();

/**
 * @title A library for calling external contracts with an address appended to the calldata.
 * @author HardlyDifficult
 */
library RouteCallLibrary {
  /**
   * @notice Routes a call to the specified contract, appending the from address to the end of the calldata.
   * If the call reverts, this will revert the transaction and the original reason is bubbled up.
   * @param from The address to use as the msg sender when calling the contract.
   * @param to The contract address to call.
   * @param callData The call data to use when calling the contract, without the sender appended.
   */
  function routeCallTo(address from, address to, bytes memory callData) internal returns (bytes memory returnData) {
    // Forward the call, with the packed from address appended, to the specified contract.
    bool success;
    (success, returnData) = tryRouteCallTo(from, to, callData);

    // If the call failed, bubble up the revert reason.
    if (!success) {
      revertWithError(returnData);
    }
  }

  /**
   * @notice Routes a call to the specified contract, appending the from address to the end of the calldata.
   * This will not revert even if the external call fails.
   * @param from The address to use as the msg sender when calling the contract.
   * @param to The contract address to call.
   * @param callData The call data to use when calling the contract, without the sender appended.
   * @dev Consumers should look for positive confirmation that if the transaction is not successful, the returned revert
   * reason is expected as an acceptable reason to ignore. Generically ignoring reverts will lead to out-of-gas errors
   * being ignored and result in unexpected behavior.
   */
  function tryRouteCallTo(
    address from,
    address to,
    bytes memory callData
  ) internal returns (bool success, bytes memory returnData) {
    // Forward the call, with the packed from address appended, to the specified contract.
    // solhint-disable-next-line avoid-low-level-calls
    (success, returnData) = to.call(abi.encodePacked(callData, from));
  }

  /**
   * @notice Bubbles up the original revert reason of a low-level call failure where possible.
   * @dev Copied from OZ's `Address.sol` library, with a minor modification to the final revert scenario.
   * This should only be used when a low-level call fails.
   */
  function revertWithError(bytes memory returnData) internal pure {
    // Look for revert reason and bubble it up if present
    if (returnData.length > 0) {
      // The easiest way to bubble the revert reason is using memory via assembly
      /// @solidity memory-safe-assembly
      assembly {
        let returnData_size := mload(returnData)
        revert(add(32, returnData), returnData_size)
      }
    } else {
      revert RouteCallLibrary_Call_Failed_Without_Revert_Reason();
    }
  }

  /**
   * @notice Extracts the appended sender address from the calldata.
   * @dev This uses the last 20 bytes of the calldata, with no guarantees that an address has indeed been appended.
   * If this is used for a call that was not routed with `routeCallTo`, the address returned will be incorrect (and
   * may be address(0)).
   */
  function extractAppendedSenderAddress() internal pure returns (address sender) {
    assembly {
      // The router appends the msg.sender to the end of the calldata
      // source: https://github.com/opengsn/gsn/blob/v3.0.0-beta.3/packages/contracts/src/ERC2771Recipient.sol#L48
      sender := shr(96, calldataload(sub(calldatasize(), 20)))
    }
  }
}

File 25 of 50 : TimeLibrary.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

/**
 * @title Helpers for working with time.
 * @author batu-inal & HardlyDifficult
 */
library TimeLibrary {
  /**
   * @notice Checks if the given timestamp is in the past.
   * @dev This helper ensures a consistent interpretation of expiry across the codebase.
   * This is different than `hasBeenReached` in that it will return false if the expiry is now.
   */
  function hasExpired(uint256 expiry) internal view returns (bool) {
    return expiry < block.timestamp;
  }

  /**
   * @notice Checks if the given timestamp is now or in the past.
   * @dev This helper ensures a consistent interpretation of expiry across the codebase.
   * This is different from `hasExpired` in that it will return true if the timestamp is now.
   */
  function hasBeenReached(uint256 timestamp) internal view returns (bool) {
    return timestamp <= block.timestamp;
  }
}

File 26 of 50 : NFTMarketCore.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

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

import "../shared/Constants.sol";
import "../shared/MarketSharedCore.sol";

error NFTMarketCore_Seller_Not_Found();
error NFTMarketCore_Can_Not_Update_Unlisted_Nft();

/**
 * @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.
 * @author batu-inal & HardlyDifficult
 */
abstract contract NFTMarketCore is ContextUpgradeable, MarketSharedCore {
  using AddressUpgradeable for address;
  using AddressUpgradeable for address payable;

  /**
   * @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*/) internal virtual {
    // No-op
  }

  /**
   * @notice Requires that an NFT is listed for sale, not in active auction, and the msg.sender is the seller which
   * listed the NFT.
   */
  function _authorizeExhibitionOrScheduleUpdate(address nftContract, uint256 tokenId) internal view {
    if (!_isAuthorizedExhibitionOrScheduleUpdate(nftContract, tokenId)) {
      revert NFTMarketCore_Can_Not_Update_Unlisted_Nft();
    }
  }

  /**
   * @notice Confirms permission to update the Exhibition or schedule for an NFT.
   * @return canUpdateNft True if the NFT is listed for sale and authorize checks did not revert.
   * @dev Verifies that the NFT is listed, not in active auction, and the sender is the owner.
   */
  function _isAuthorizedExhibitionOrScheduleUpdate(
    address /*nftContract*/,
    uint256 /*tokenId*/
  ) internal view virtual returns (bool canUpdateNft) {
    // False by default, may be set to true by a market tool mixin if the NFT is listed.
  }

  /**
   * @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 originalSeller
  ) internal virtual {
    _transferFromEscrow(nftContract, tokenId, originalSeller, address(0));
  }

  /**
   * @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(_msgSender(), address(this), tokenId);
  }

  /**
   * @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;
  }

  /**
   * @inheritdoc MarketSharedCore
   */
  function _getSellerOrOwnerOf(
    address nftContract,
    uint256 tokenId
  ) internal view override returns (address payable sellerOrOwner) {
    sellerOrOwner = _getSellerOf(nftContract, tokenId);
    if (sellerOrOwner == address(0)) {
      sellerOrOwner = 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[450] private __gap;
}

File 27 of 50 : NFTMarketScheduling.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

import "../../libraries/TimeLibrary.sol";

import "../../interfaces/internal/routes/INFTMarketScheduling.sol";
import "../../interfaces/internal/INFTMarketGetters.sol";

import "../shared/Constants.sol";
import "../shared/MarketFees.sol";

import "./NFTMarketCore.sol";

// Errors when configuring a schedule
error NFTMarketScheduling_Sale_Starts_At_Already_Set();
error NFTMarketScheduling_Sale_Starts_At_Is_In_Past();
error NFTMarketScheduling_Sale_Starts_At_Too_Far_In_The_Future(uint256 maxStartsAt);

// Errors when validating a schedule
error NFTMarketScheduling_Sale_Starts_At_Is_In_Future();

/**
 * @title Allows listed NFTs to schedule a sale starts at time.
 * @dev This supports both Auctions and BuyNow.
 * @author HardlyDifficult & smhutch
 */
abstract contract NFTMarketScheduling is
  INFTMarketGetters,
  INFTMarketScheduling,
  ContextUpgradeable,
  NFTMarketCore,
  MarketFees
{
  using TimeLibrary for uint256;

  /// @notice Stores the saleStartsAt time for listed NFTs
  mapping(address => mapping(uint256 => uint256)) private $nftContractToTokenIdToSaleStartsAt;

  /**
   * @notice emitted when an a saleStartsAt time is changed for an NFT.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param operator The address that triggered this change.
   * @param saleStartsAt The time at which the NFT will be available to buy or place bids on.
   *  When zero, this represents that the NFT is unscheduled.
   *  When above zero, this value represents the time in seconds since the Unix epoch.
   */
  event SetSaleStartsAt(
    address indexed nftContract,
    uint256 indexed tokenId,
    address indexed operator,
    uint256 saleStartsAt
  );

  ////////////////////////////////////////////////////////////////
  // Configuration
  ////////////////////////////////////////////////////////////////

  /**
   * @notice sets the saleStartsAt time for a listed NFT.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param saleStartsAt The time at which the NFT will be available to buy or place bids on.
   *  When zero, the NFT has no saleStartsAt and can be purchased anytime.
   *  When above zero, this value represents the time in seconds since the Unix epoch.
   */
  function setSaleStartsAt(address nftContract, uint256 tokenId, uint256 saleStartsAt) external {
    // Check if it's already set first since this may be a common occurrence.
    if ($nftContractToTokenIdToSaleStartsAt[nftContract][tokenId] == saleStartsAt) {
      revert NFTMarketScheduling_Sale_Starts_At_Already_Set();
    }

    _authorizeExhibitionOrScheduleUpdate(nftContract, tokenId);
    if (saleStartsAt != 0) {
      if (saleStartsAt.hasExpired()) {
        revert NFTMarketScheduling_Sale_Starts_At_Is_In_Past();
      }

      if (saleStartsAt > block.timestamp + MAX_SCHEDULED_TIME_IN_THE_FUTURE) {
        // Prevent arbitrarily large values from accidentally being set.
        revert NFTMarketScheduling_Sale_Starts_At_Too_Far_In_The_Future(
          block.timestamp + MAX_SCHEDULED_TIME_IN_THE_FUTURE
        );
      }
    }

    $nftContractToTokenIdToSaleStartsAt[nftContract][tokenId] = saleStartsAt;

    emit SetSaleStartsAt({
      nftContract: nftContract,
      tokenId: tokenId,
      operator: _msgSender(),
      saleStartsAt: saleStartsAt
    });
  }

  /**
   * @notice Returns the saleStartsAt time for a listed NFT.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @return saleStartsAt The time at which the NFT will be available to buy or place bids on.
   * 0 if there is no schedule set and the NFT may be purchased anytime (or is not yet listed).
   */
  function getSaleStartsAt(address nftContract, uint256 tokenId) external view returns (uint256 saleStartsAt) {
    saleStartsAt = $nftContractToTokenIdToSaleStartsAt[nftContract][tokenId];
  }

  ////////////////////////////////////////////////////////////////
  // Validation
  ////////////////////////////////////////////////////////////////

  function _validateSaleStartsAtHasBeenReached(address nftContract, uint256 tokenId) internal view {
    if (!$nftContractToTokenIdToSaleStartsAt[nftContract][tokenId].hasBeenReached()) {
      revert NFTMarketScheduling_Sale_Starts_At_Is_In_Future();
    }
  }

  /**
   * @inheritdoc NFTMarketCore
   * @dev Validates the saleStartsAt time for the NFT when the first bid is placed.
   */
  function _beforeAuctionStarted(address nftContract, uint256 tokenId) internal virtual override {
    _validateSaleStartsAtHasBeenReached(nftContract, tokenId);
    super._beforeAuctionStarted(nftContract, tokenId);
  }

  ////////////////////////////////////////////////////////////////
  // Cleanup
  ////////////////////////////////////////////////////////////////

  function _clearScheduleIfSet(address nftContract, uint256 tokenId) private {
    if ($nftContractToTokenIdToSaleStartsAt[nftContract][tokenId] != 0) {
      // Clear the saleStartsAt time so that it does not apply to the next listing
      delete $nftContractToTokenIdToSaleStartsAt[nftContract][tokenId];
      emit SetSaleStartsAt({ nftContract: nftContract, tokenId: tokenId, operator: _msgSender(), saleStartsAt: 0 });
    }
  }

  /**
   * @dev When a sale occurs, clear the schedule if one was set.
   */
  function _distributeFunds(
    address nftContract,
    uint256 tokenId,
    address payable seller,
    uint256 price,
    address payable buyReferrer,
    address payable sellerReferrerPaymentAddress,
    uint16 sellerReferrerTakeRateInBasisPoints
  ) internal virtual override returns (uint256 totalFees, uint256 creatorRev, uint256 sellerRev) {
    _clearScheduleIfSet(nftContract, tokenId);

    (totalFees, creatorRev, sellerRev) = super._distributeFunds(
      nftContract,
      tokenId,
      seller,
      price,
      buyReferrer,
      sellerReferrerPaymentAddress,
      sellerReferrerTakeRateInBasisPoints
    );
  }

  /**
   * @dev Called when a listing is canceled. This mixin appears before the market tools in inheritance order, so when
   * this is called we have already confirmed that the NFT is no longer listed and will indeed leave escrow.
   */
  function _transferFromEscrowIfAvailable(
    address nftContract,
    uint256 tokenId,
    address originalSeller
  ) internal virtual override {
    _clearScheduleIfSet(nftContract, tokenId);

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

  /**
   * @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 This mixin uses 250 slots in total.
   */
  uint256[249] private __gap;
}

File 28 of 50 : NFTCollectionFactoryRouterAPIs.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "../../../interfaces/internal/routes/INFTCollectionFactoryDrops.sol";
import "../../../interfaces/internal/routes/INFTCollectionFactoryTimedEditions.sol";
import "../../../interfaces/internal/routes/INFTDropMarketFixedPriceSale.sol";
import "../../../interfaces/internal/routes/INFTCollectionFactoryLimitedEditions.sol";
import "../../../libraries/AddressLibrary.sol";
import "../../../libraries/RouteCallLibrary.sol";

import "../../shared/NFTDropMarketNode.sol";
import "../../shared/NFTCollectionFactoryNode.sol";

/// @notice Parameters used to create a drop collection.
struct DropCollectionCreationParams {
  /// @notice The collection's `name`.
  string name;
  /// @notice The collection's `symbol`.
  string symbol;
  /// @notice The base URI for the collection.
  string baseURI;
  /// @notice Whether the collection is revealed or not.
  bool isRevealed;
  /// @notice The max `tokenID` for this collection.
  uint32 maxTokenId;
  /// @notice The nonce used by the creator to create this collection.
  uint96 nonce;
}

/// @notice Parameters used to create a limited edition collection.
struct LimitedEditionCollectionCreationParams {
  /// @notice The collection's `name`.
  string name;
  /// @notice The collection's `symbol`.
  string symbol;
  /// @notice The token URI for the collection.
  string tokenURI;
  /// @notice The max `tokenID` for this collection.
  uint32 maxTokenId;
  /// @notice The nonce used by the creator to create this collection.
  uint96 nonce;
}

/// @notice Parameters used to create a timed edition collection.
struct TimedEditionCollectionCreationParams {
  /// @notice The collection's `name`.
  string name;
  /// @notice The collection's `symbol`.
  string symbol;
  /// @notice The token URI for the collection.
  string tokenURI;
  /// @notice The nonce used by the creator to create this collection.
  uint96 nonce;
}

/**
 * @title Wraps external calls to the NFTCollectionFactory contract.
 * @dev Each call uses standard APIs and params, along with the msg.sender appended to the calldata. They will decode
 * return values as appropriate. If any of these calls fail, the tx will revert with the original reason.
 * @author HardlyDifficult & reggieag
 */
abstract contract NFTCollectionFactoryRouterAPIs is NFTCollectionFactoryNode, NFTDropMarketNode {
  using RouteCallLibrary for address;

  function _createNFTDropCollection(
    DropCollectionCreationParams calldata collectionParams
  ) internal returns (address collection) {
    bytes memory returnData = msg.sender.routeCallTo(
      nftCollectionFactory,
      abi.encodeCall(
        INFTCollectionFactoryDrops.createNFTDropCollection,
        (
          collectionParams.name,
          collectionParams.symbol,
          collectionParams.baseURI,
          collectionParams.isRevealed,
          collectionParams.maxTokenId,
          nftDropMarket,
          collectionParams.nonce
        )
      )
    );
    collection = abi.decode(returnData, (address));
  }

  function _createNFTDropCollectionWithPaymentFactory(
    DropCollectionCreationParams calldata collectionParams,
    CallWithoutValue calldata paymentAddressFactoryCall
  ) internal returns (address collection) {
    bytes memory returnData = msg.sender.routeCallTo(
      nftCollectionFactory,
      abi.encodeCall(
        INFTCollectionFactoryDrops.createNFTDropCollectionWithPaymentFactory,
        (
          collectionParams.name,
          collectionParams.symbol,
          collectionParams.baseURI,
          collectionParams.isRevealed,
          collectionParams.maxTokenId,
          nftDropMarket,
          collectionParams.nonce,
          paymentAddressFactoryCall
        )
      )
    );
    collection = abi.decode(returnData, (address));
  }

  function _createNFTTimedEditionCollection(
    string calldata name,
    string calldata symbol,
    string calldata tokenURI,
    uint256 mintEndTime,
    address approvedMinter,
    uint96 nonce
  ) internal returns (address collection) {
    bytes memory returnData = msg.sender.routeCallTo(
      nftCollectionFactory,
      abi.encodeCall(
        INFTCollectionFactoryTimedEditions.createNFTTimedEditionCollection,
        (name, symbol, tokenURI, mintEndTime, approvedMinter, nonce)
      )
    );
    collection = abi.decode(returnData, (address));
  }

  function _createNFTTimedEditionCollectionWithPaymentFactory(
    string calldata name,
    string calldata symbol,
    string calldata tokenURI,
    uint256 mintEndTime,
    address approvedMinter,
    uint96 nonce,
    CallWithoutValue calldata paymentAddressFactoryCall
  ) internal returns (address collection) {
    bytes memory returnData = msg.sender.routeCallTo(
      nftCollectionFactory,
      abi.encodeCall(
        INFTCollectionFactoryTimedEditions.createNFTTimedEditionCollectionWithPaymentFactory,
        (name, symbol, tokenURI, mintEndTime, approvedMinter, nonce, paymentAddressFactoryCall)
      )
    );
    collection = abi.decode(returnData, (address));
  }

  function _createNFTLimitedEditionCollection(
    LimitedEditionCollectionCreationParams calldata collectionParams
  ) internal returns (address collection) {
    bytes memory returnData = msg.sender.routeCallTo(
      nftCollectionFactory,
      abi.encodeCall(
        INFTCollectionFactoryLimitedEditions.createNFTLimitedEditionCollection,
        (
          collectionParams.name,
          collectionParams.symbol,
          collectionParams.tokenURI,
          collectionParams.maxTokenId,
          nftDropMarket,
          collectionParams.nonce
        )
      )
    );
    collection = abi.decode(returnData, (address));
  }

  function _createNFTLimitedEditionCollectionWithPaymentFactory(
    LimitedEditionCollectionCreationParams calldata collectionParams,
    CallWithoutValue calldata paymentAddressFactoryCall
  ) internal returns (address collection) {
    bytes memory returnData = msg.sender.routeCallTo(
      nftCollectionFactory,
      abi.encodeCall(
        INFTCollectionFactoryLimitedEditions.createNFTLimitedEditionCollectionWithPaymentFactory,
        (
          collectionParams.name,
          collectionParams.symbol,
          collectionParams.tokenURI,
          collectionParams.maxTokenId,
          nftDropMarket,
          collectionParams.nonce,
          paymentAddressFactoryCall
        )
      )
    );
    collection = abi.decode(returnData, (address));
  }
}

File 29 of 50 : NFTDropMarketRouterAPIs.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "../../../interfaces/internal/routes/INFTDropMarketFixedPriceSale.sol";
import { INFTDropMarketDutchAuction } from "../../../interfaces/internal/routes/INFTDropMarketDutchAuction.sol";
import "../../../libraries/RouteCallLibrary.sol";

import "../../shared/NFTDropMarketNode.sol";

/// @notice Parameters used to create a fixed price sale.
struct FixedPriceSaleParams {
  /// @notice The World NFT or exhibition ID to associate this fix priced sale to.
  /// Set this to 0 to exist outside of a World/exhibition.
  uint256 worldOrExhibitionId;
  /// @notice The fixed price per NFT in the collection.
  uint256 price;
  /// @notice The max number of NFTs an account may mint in this sale.
  uint256 limitPerAccount;
  /// @notice The start time of the general availability period, in seconds since the Unix epoch.
  /// @dev When set to 0, general availability is set to the block timestamp the transaction is mined.
  uint256 generalAvailabilityStartTime;
}

/// @notice Parameters used to create a fixed price sale.
struct FixedPriceSaleParamsV2 {
  /// @notice The World NFT or exhibition ID to associate this fix priced sale to.
  /// Set this to 0 to exist outside of a World/exhibition.
  uint256 worldOrExhibitionId;
  /// @notice The fixed price per NFT in the collection.
  uint256 price;
  /// @notice The max number of NFTs an account may mint in this sale.
  uint256 limitPerAccount;
  /// @notice The start time of the general availability period, in seconds since the Unix epoch.
  /// @dev When set to 0, general availability is set to the block timestamp the transaction is mined.
  uint256 generalAvailabilityStartTime;
  /// @notice The sale duration in seconds.
  uint256 saleDuration;
}

/**
 * @title Wraps external calls to the NFTDropMarket contract.
 * @dev Each call uses standard APIs and params, along with the msg.sender appended to the calldata. They will decode
 * return values as appropriate. If any of these calls fail, the tx will revert with the original reason.
 * @author HardlyDifficult & reggieag
 */
abstract contract NFTDropMarketRouterAPIs is NFTDropMarketNode {
  using RouteCallLibrary for address;

  function _createFixedPriceSaleV3(
    address nftContract,
    FixedPriceSaleParams memory fixedPriceSaleParams,
    uint256 txDeadlineTime
  ) internal {
    msg.sender.routeCallTo(
      nftDropMarket,
      abi.encodeCall(
        INFTDropMarketFixedPriceSale.createFixedPriceSaleV3,
        (
          nftContract,
          fixedPriceSaleParams.worldOrExhibitionId,
          fixedPriceSaleParams.price,
          fixedPriceSaleParams.limitPerAccount,
          fixedPriceSaleParams.generalAvailabilityStartTime,
          txDeadlineTime
        )
      )
    );
  }

  function _createFixedPriceSaleWithEarlyAccessAllowlistV2(
    address nftContract,
    FixedPriceSaleParams memory fixedPriceSaleParams,
    uint256 earlyAccessStartTime,
    bytes32 merkleRoot,
    string calldata merkleTreeUri,
    uint256 txDeadlineTime
  ) internal {
    msg.sender.routeCallTo(
      nftDropMarket,
      abi.encodeCall(
        INFTDropMarketFixedPriceSale.createFixedPriceSaleWithEarlyAccessAllowlistV2,
        (
          nftContract,
          fixedPriceSaleParams.worldOrExhibitionId,
          fixedPriceSaleParams.price,
          fixedPriceSaleParams.limitPerAccount,
          fixedPriceSaleParams.generalAvailabilityStartTime,
          earlyAccessStartTime,
          merkleRoot,
          merkleTreeUri,
          txDeadlineTime
        )
      )
    );
  }

  function _createLinearDutchAuction(
    address nftContract,
    uint256 worldOrExhibitionId,
    uint256 maxPrice,
    uint256 minPrice,
    uint256 limitPerAccount,
    uint256 startTime,
    uint256 saleDuration
  ) internal {
    msg.sender.routeCallTo(
      nftDropMarket,
      abi.encodeCall(
        INFTDropMarketDutchAuction.createLinearDutchAuction,
        (nftContract, worldOrExhibitionId, maxPrice, minPrice, limitPerAccount, startTime, saleDuration)
      )
    );
  }
}

File 30 of 50 : NFTMarketRouterAPIs.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "../../../interfaces/internal/routes/INFTMarketBuyNow.sol";
import "../../../interfaces/internal/routes/INFTMarketExhibition.sol";
import "../../../interfaces/internal/routes/INFTMarketReserveAuction.sol";
import "../../../interfaces/internal/routes/INFTMarketScheduling.sol";
import "../../../libraries/RouteCallLibrary.sol";
import { NFTMarketScheduling_Sale_Starts_At_Already_Set } from "../../nftMarket/NFTMarketScheduling.sol";
import "../../shared/NFTMarketNode.sol";

/**
 * @title Wraps external calls to the NFTMarket contract.
 * @dev Each call uses standard APIs and params, along with the msg.sender appended to the calldata. They will decode
 * return values as appropriate. If any of these calls fail, the tx will revert with the original reason.
 * @author HardlyDifficult, reggieag
 */
abstract contract NFTMarketRouterAPIs is NFTMarketNode {
  using RouteCallLibrary for address;

  ////////////////////////////////////////////////////////////////
  // Auctions
  ////////////////////////////////////////////////////////////////

  function _cancelReserveAuction(uint256 auctionId) internal {
    msg.sender.routeCallTo(nftMarket, abi.encodeCall(INFTMarketReserveAuction.cancelReserveAuction, (auctionId)));
  }

  function _createReserveAuctionV3(
    address nftContract,
    uint256 tokenId,
    uint256 worldOrExhibitionId,
    uint256 reservePrice,
    uint256 duration
  ) internal returns (uint256 auctionId) {
    bytes memory returnData = msg.sender.routeCallTo(
      nftMarket,
      abi.encodeCall(
        INFTMarketReserveAuction.createReserveAuctionV3,
        (nftContract, tokenId, worldOrExhibitionId, reservePrice, duration)
      )
    );
    auctionId = abi.decode(returnData, (uint256));
  }

  function _updateReserveAuctionV2(uint256 auctionId, uint256 worldOrExhibitionId, uint256 reservePrice) internal {
    msg.sender.routeCallTo(
      nftMarket,
      abi.encodeCall(INFTMarketReserveAuction.updateReserveAuctionV2, (auctionId, worldOrExhibitionId, reservePrice))
    );
  }

  ////////////////////////////////////////////////////////////////
  // Buy Now
  ////////////////////////////////////////////////////////////////

  function _cancelBuyPrice(address nftContract, uint256 tokenId) internal {
    msg.sender.routeCallTo(nftMarket, abi.encodeCall(INFTMarketBuyNow.cancelBuyPrice, (nftContract, tokenId)));
  }

  function _setBuyPriceV2(address nftContract, uint256 tokenId, uint256 worldOrExhibitionId, uint256 price) internal {
    msg.sender.routeCallTo(
      nftMarket,
      abi.encodeCall(INFTMarketBuyNow.setBuyPriceV2, (nftContract, tokenId, worldOrExhibitionId, price))
    );
  }

  ////////////////////////////////////////////////////////////////
  // Exhibitions
  ////////////////////////////////////////////////////////////////

  function _updateNftExhibition(address nftContract, uint256 tokenId, uint256 worldOrExhibitionId) internal {
    msg.sender.routeCallTo(
      nftMarket,
      abi.encodeCall(INFTMarketExhibitionForRouter.updateExhibitionNft, (nftContract, tokenId, worldOrExhibitionId))
    );
  }

  ////////////////////////////////////////////////////////////////
  // Scheduling
  ////////////////////////////////////////////////////////////////

  function _setSaleStartsAt(address nftContract, uint256 tokenId, uint256 saleStartsAt) internal {
    msg.sender.routeCallTo(
      nftMarket,
      abi.encodeCall(INFTMarketScheduling.setSaleStartsAt, (nftContract, tokenId, saleStartsAt))
    );
  }

  function _setSaleStartsAtAndIgnoreErrorIfStartsAtAlreadySet(
    address nftContract,
    uint256 tokenId,
    uint256 saleStartsAt
  ) internal {
    (bool success, bytes memory returnData) = msg.sender.tryRouteCallTo(
      nftMarket,
      abi.encodeCall(INFTMarketScheduling.setSaleStartsAt, (nftContract, tokenId, saleStartsAt))
    );

    if (!success && bytes4(returnData) != NFTMarketScheduling_Sale_Starts_At_Already_Set.selector) {
      RouteCallLibrary.revertWithError(returnData);
    }
  }
}

File 31 of 50 : WorldsNftRouterAPIs.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "../../../interfaces/internal/routes/IWorldsNftUserRoles.sol";
import "../../../libraries/RouteCallLibrary.sol";

import "../../shared/WorldsNftNode.sol";

abstract contract WorldsNftRouterAPIs is WorldsNftNode {
  using RouteCallLibrary for address;

  function _revokeAllRolesForUser(uint256 worldId, address user) internal {
    msg.sender.routeCallTo(worlds, abi.encodeCall(IWorldsNftUserRoles.revokeAllRolesForUser, (worldId, user)));
  }

  function _setAdminRole(uint256 worldId, address user) internal {
    msg.sender.routeCallTo(worlds, abi.encodeCall(IWorldsNftUserRoles.setAdminRole, (worldId, user)));
  }

  function _setEditorRole(uint256 worldId, address user) internal {
    msg.sender.routeCallTo(worlds, abi.encodeCall(IWorldsNftUserRoles.setEditorRole, (worldId, user)));
  }
}

File 32 of 50 : NFTCreateAndListLimitedEditionCollection.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "../../libraries/AddressLibrary.sol";
import "../shared/TxDeadline.sol";

import "./apis/NFTCollectionFactoryRouterAPIs.sol";
import "./apis/NFTDropMarketRouterAPIs.sol";

/**
 * @title Offers value-added functions for creating limited edition collections using the NFTCollectionFactory contract
 * and creating sales using the NFTDropMarket contract.
 * An example of a value-added function is the ability to create a collection and sale in a single transaction.
 * @author gosseti
 */
abstract contract NFTCreateAndListLimitedEditionCollection is
  TxDeadline,
  NFTCollectionFactoryRouterAPIs,
  NFTDropMarketRouterAPIs
{
  /**
   * @notice Create a new limited edition collection contract and fixed price sale.
   * @param collectionParams The parameters for the limited edition collection creation.
   * @param fixedPriceSaleParams The parameters for the fixed price sale creation.
   * @param txDeadlineTime The deadline timestamp for the transaction to be mined, in seconds since Unix epoch.
   * @return collection The address of the newly created collection contract.
   * @dev The collection will include the `nftDropMarket` as an approved minter.
   */
  function createLimitedEditionCollectionAndFixedPriceSale(
    LimitedEditionCollectionCreationParams calldata collectionParams,
    FixedPriceSaleParams calldata fixedPriceSaleParams,
    uint256 txDeadlineTime
  ) external txDeadlineNotExpired(txDeadlineTime) returns (address collection) {
    collection = _createNFTLimitedEditionCollection({ collectionParams: collectionParams });
    _createFixedPriceSaleV3({
      nftContract: collection,
      fixedPriceSaleParams: fixedPriceSaleParams,
      // The deadline provided has already been validated above.
      txDeadlineTime: 0
    });
  }

  /**
   * @notice Create a new limited edition collection contract with a payment factory and fixed price sale.
   * @param collectionParams The parameters for the limited edition collection creation.
   * @param paymentAddressFactoryCall The contract call which will return the address to use for payments.
   * @param fixedPriceSaleParams The parameters for the fixed price sale creation.
   * @param txDeadlineTime The deadline timestamp for the transaction to be mined, in seconds since Unix epoch.
   * @return collection The address of the newly created collection contract.
   * @dev The collection will include the `nftDropMarket` as an approved minter.
   */
  function createLimitedEditionCollectionAndFixedPriceSaleWithPaymentFactory(
    LimitedEditionCollectionCreationParams calldata collectionParams,
    CallWithoutValue calldata paymentAddressFactoryCall,
    FixedPriceSaleParams calldata fixedPriceSaleParams,
    uint256 txDeadlineTime
  ) external txDeadlineNotExpired(txDeadlineTime) returns (address collection) {
    collection = _createNFTLimitedEditionCollectionWithPaymentFactory({
      collectionParams: collectionParams,
      paymentAddressFactoryCall: paymentAddressFactoryCall
    });
    _createFixedPriceSaleV3({
      nftContract: collection,
      fixedPriceSaleParams: fixedPriceSaleParams,
      // The deadline provided has already been validated above.
      txDeadlineTime: 0
    });
  }
}

File 33 of 50 : NFTCreateAndListTimedEditionCollection.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "../../libraries/AddressLibrary.sol";
import "../shared/TxDeadline.sol";

import "./apis/NFTCollectionFactoryRouterAPIs.sol";
import "./apis/NFTDropMarketRouterAPIs.sol";

error NFTCreateAndListTimedEditionCollection_Sale_Duration_Cannot_Be_Zero();

/**
 * @title Offers value-added functions for creating edition collections using the NFTCollectionFactory contract
 * and creating sales using the NFTDropMarket contract.
 * An example of a value-added function is the ability to create a collection and sale in a single transaction.
 * @author reggieag & HardlyDifficult & gosseti
 */
abstract contract NFTCreateAndListTimedEditionCollection is
  TxDeadline,
  NFTCollectionFactoryRouterAPIs,
  NFTDropMarketRouterAPIs
{
  /**
   * @notice Create a new edition collection contract and timed sale.
   * @param collectionParams The parameters for the edition collection creation.
   * @param fixedPriceSaleParams  The parameters for the sale creation.
   * @param txDeadlineTime The deadline timestamp for the transaction to be mined, in seconds since Unix epoch.
   * @return collection The address of the newly created collection contract.
   * @dev The collection will include the `nftDropMarket` as an approved minter.
   */
  function createTimedEditionCollectionAndFixedPriceSaleV2(
    TimedEditionCollectionCreationParams calldata collectionParams,
    FixedPriceSaleParamsV2 calldata fixedPriceSaleParams,
    uint256 txDeadlineTime
  ) external txDeadlineNotExpired(txDeadlineTime) returns (address collection) {
    if (fixedPriceSaleParams.saleDuration == 0) {
      revert NFTCreateAndListTimedEditionCollection_Sale_Duration_Cannot_Be_Zero();
    }
    uint256 generalAvailabilityStartTime = fixedPriceSaleParams.generalAvailabilityStartTime;
    if (generalAvailabilityStartTime == 0) {
      generalAvailabilityStartTime = block.timestamp;
    }
    collection = _createNFTTimedEditionCollection({
      name: collectionParams.name,
      symbol: collectionParams.symbol,
      tokenURI: collectionParams.tokenURI,
      mintEndTime: generalAvailabilityStartTime + fixedPriceSaleParams.saleDuration,
      approvedMinter: nftDropMarket,
      nonce: collectionParams.nonce
    });

    FixedPriceSaleParams memory mappedFixedPriceSaleParams = FixedPriceSaleParams({
      worldOrExhibitionId: fixedPriceSaleParams.worldOrExhibitionId,
      price: fixedPriceSaleParams.price,
      limitPerAccount: fixedPriceSaleParams.limitPerAccount,
      generalAvailabilityStartTime: fixedPriceSaleParams.generalAvailabilityStartTime
    });

    _createFixedPriceSaleV3({
      nftContract: collection,
      fixedPriceSaleParams: mappedFixedPriceSaleParams,
      // The deadline provided has already been validated above.
      txDeadlineTime: 0
    });
  }

  /**
   * @notice Create a new edition collection contract and timed sale with a payment factory.
   * @param collectionParams The parameters for the edition collection creation.
   * @param paymentAddressFactoryCall The contract call which will return the address to use for payments.
   * @param fixedPriceSaleParams  The parameters for the sale creation.
   * @param txDeadlineTime The deadline timestamp for the transaction to be mined, in seconds since Unix epoch.
   * @return collection The address of the newly created collection contract.
   * @dev The collection will include the `nftDropMarket` as an approved minter.
   */
  function createTimedEditionCollectionAndFixedPriceSaleWithPaymentFactoryV2(
    TimedEditionCollectionCreationParams calldata collectionParams,
    CallWithoutValue calldata paymentAddressFactoryCall,
    FixedPriceSaleParamsV2 calldata fixedPriceSaleParams,
    uint256 txDeadlineTime
  ) external txDeadlineNotExpired(txDeadlineTime) returns (address collection) {
    if (fixedPriceSaleParams.saleDuration == 0) {
      revert NFTCreateAndListTimedEditionCollection_Sale_Duration_Cannot_Be_Zero();
    }
    uint256 generalAvailabilityStartTime = fixedPriceSaleParams.generalAvailabilityStartTime;
    if (generalAvailabilityStartTime == 0) {
      generalAvailabilityStartTime = block.timestamp;
    }
    collection = _createNFTTimedEditionCollectionWithPaymentFactory({
      name: collectionParams.name,
      symbol: collectionParams.symbol,
      tokenURI: collectionParams.tokenURI,
      mintEndTime: generalAvailabilityStartTime + fixedPriceSaleParams.saleDuration,
      approvedMinter: nftDropMarket,
      nonce: collectionParams.nonce,
      paymentAddressFactoryCall: paymentAddressFactoryCall
    });

    FixedPriceSaleParams memory mappedFixedPriceSaleParams = FixedPriceSaleParams({
      worldOrExhibitionId: fixedPriceSaleParams.worldOrExhibitionId,
      price: fixedPriceSaleParams.price,
      limitPerAccount: fixedPriceSaleParams.limitPerAccount,
      generalAvailabilityStartTime: fixedPriceSaleParams.generalAvailabilityStartTime
    });

    _createFixedPriceSaleV3({
      nftContract: collection,
      fixedPriceSaleParams: mappedFixedPriceSaleParams,
      // The deadline provided has already been validated above.
      txDeadlineTime: 0
    });
  }
}

File 34 of 50 : NFTMarketRouterCore.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

/**
 * @title A placeholder for any shared logic for NFT Market Router mixins.
 * @author HardlyDifficult
 */
abstract contract NFTMarketRouterCore {}

File 35 of 50 : NFTMarketRouterCreateFixedPriceSale.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import "./apis/NFTDropMarketRouterAPIs.sol";

abstract contract NFTMarketRouterCreateFixedPriceSale is NFTDropMarketRouterAPIs {
  function createFixedPriceSale(
    address nftContract,
    FixedPriceSaleParams calldata fixedPriceSaleParams,
    uint256 txDeadlineTime
  ) external {
    _createFixedPriceSaleV3(nftContract, fixedPriceSaleParams, txDeadlineTime);
  }

  function createFixedPriceSaleWithEarlyAccessAllowlist(
    address nftContract,
    FixedPriceSaleParams calldata fixedPriceSaleParams,
    uint256 earlyAccessStartTime,
    bytes32 merkleRoot,
    string calldata merkleTreeUri,
    uint256 txDeadlineTime
  ) external {
    _createFixedPriceSaleWithEarlyAccessAllowlistV2(
      nftContract,
      fixedPriceSaleParams,
      earlyAccessStartTime,
      merkleRoot,
      merkleTreeUri,
      txDeadlineTime
    );
  }
}

File 36 of 50 : NFTMarketRouterDutchAuction.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

import { NFTDropMarketRouterAPIs } from "./apis/NFTDropMarketRouterAPIs.sol";

abstract contract NFTMarketRouterDutchAuction is NFTDropMarketRouterAPIs {
  function createLinearDutchAuction(
    address nftContract,
    uint256 worldOrExhibitionId,
    uint256 maxPrice,
    uint256 minPrice,
    uint256 limitPerAccount,
    uint256 startTime,
    uint256 saleDuration
  ) external {
    _createLinearDutchAuction(
      nftContract,
      worldOrExhibitionId,
      maxPrice,
      minPrice,
      limitPerAccount,
      startTime,
      saleDuration
    );
  }
}

File 37 of 50 : NFTMarketRouterList.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "../../interfaces/internal/INFTMarketGetters.sol";

import "./apis/NFTMarketRouterAPIs.sol";

error NFTMarketRouterList_Buy_Price_Set_But_Should_Set_Buy_Price_Is_False();
error NFTMarketRouterList_Duration_Set_Without_Reserve_Price();
error NFTMarketRouterList_Exhibition_Id_Set_Without_Prices();
error NFTMarketRouterList_Must_Set_Reserve_Or_Buy_Price();
error NFTMarketRouterList_Must_Set_Reserve_Buy_Price_Or_Exhibition();
error NFTMarketRouterList_Schedule_Set_Without_Prices();
error NFTMarketRouterList_Token_Ids_Not_Set();

/**
 * @title Offers value-added functions for listing NFTs in the NFTMarket contract.
 * @author batu-inal & HardlyDifficult & reggieag
 */
abstract contract NFTMarketRouterList is NFTMarketRouterAPIs {
  using RouteCallLibrary for address;

  /**
   * @notice [Deprecated] Use `batchListFromCollectionV3` instead.
   * Batch create reserve auction and/or set a buy price for many NFTs and escrow in the market contract.
   * A reserve auction price and/or a buy price must be set.
   * @param nftContract The address of the NFT contract.
   * @param tokenIds The ids of NFTs from the collection to set prices for.
   * @param worldOrExhibitionId The World NFT or exhibition ID the auctions are to be listed with.
   * Set this to 0 if n/a.
   * @param reservePrice The initial reserve price for the auctions created.
   * Set the reservePrice to 0 to skip creating auctions.
   * @param auctionDuration The duration of the auctions created.
   * Set this to 0 to use the default duration of 24 hours.
   * @param shouldSetBuyPrice True if buy prices should be set for these NFTs.
   * Set this to false to skip setting buy prices. 0 is a valid buy price enabling a giveaway.
   * @param buyPrice The price at which someone could buy these NFTs.
   * @return firstAuctionIdOfSequence 0 if reservePrice is 0, otherwise this is the id of the first auction listed.
   * The other auctions in the batch are listed sequentially from `first id` to `first id + count`.
   * @dev Notes:
   *   a) Approval should be granted for the NFTMarket contract before using this function.
   *   b) If any NFT is already listed for auction/buy now then the entire batch call will revert.
   */
  function batchListFromCollectionV2(
    address nftContract,
    uint256[] calldata tokenIds,
    uint256 worldOrExhibitionId,
    uint256 reservePrice,
    uint256 auctionDuration,
    bool shouldSetBuyPrice,
    uint256 buyPrice
  ) public returns (uint256 firstAuctionIdOfSequence) {
    // Validate input.
    if (tokenIds.length == 0) {
      revert NFTMarketRouterList_Token_Ids_Not_Set();
    }
    if (!shouldSetBuyPrice && buyPrice != 0) {
      revert NFTMarketRouterList_Buy_Price_Set_But_Should_Set_Buy_Price_Is_False();
    }

    // List NFTs for sale.
    if (reservePrice != 0) {
      // Create auctions.

      // Process the first NFT in order to capture that auction ID as the return value.
      firstAuctionIdOfSequence = _createReserveAuctionV3(
        nftContract,
        tokenIds[0],
        worldOrExhibitionId,
        reservePrice,
        auctionDuration
      );
      if (shouldSetBuyPrice) {
        // And set buy prices.
        _setBuyPriceV2(nftContract, tokenIds[0], worldOrExhibitionId, buyPrice);
      }

      for (uint256 i = 1; i < tokenIds.length; ) {
        _createReserveAuctionV3(nftContract, tokenIds[i], worldOrExhibitionId, reservePrice, auctionDuration);
        if (shouldSetBuyPrice) {
          _setBuyPriceV2(nftContract, tokenIds[i], worldOrExhibitionId, buyPrice);
        }
        unchecked {
          ++i;
        }
      }
    } else {
      // Set buy prices only (no auctions).
      if (auctionDuration != 0) {
        // Duration is only for auctions.
        revert NFTMarketRouterList_Duration_Set_Without_Reserve_Price();
      }
      if (!shouldSetBuyPrice) {
        revert NFTMarketRouterList_Must_Set_Reserve_Or_Buy_Price();
      }

      for (uint256 i = 0; i < tokenIds.length; ) {
        _setBuyPriceV2(nftContract, tokenIds[i], worldOrExhibitionId, buyPrice);
        unchecked {
          ++i;
        }
      }
    }
  }

  /**
   * @notice Batch create reserve auction and/or set a buy price for many NFTs and escrow in the market contract.
   * A reserve auction price and/or a buy price must be set.
   * @param nftContract The address of the NFT contract.
   * @param tokenIds The ids of NFTs from the collection to set prices for.
   * @param worldOrExhibitionId The World NFT or exhibition ID the auctions are to be listed with.
   * Set this to 0 if n/a.
   * @param reservePrice The initial reserve price for the auctions created.
   * Set the reservePrice to 0 to skip creating auctions.
   * @param auctionDuration The duration of the auctions created.
   * Set this to 0 to use the default duration of 24 hours.
   * @param shouldSetBuyPrice True if buy prices should be set for these NFTs.
   * Set this to false to skip setting buy prices. 0 is a valid buy price enabling a giveaway.
   * @param buyPrice The price at which someone could buy these NFTs.
   * @param saleStartsAt The time the sale should start at. 0 if the sale should start immediately.
   * @return firstAuctionIdOfSequence 0 if reservePrice is 0, otherwise this is the id of the first auction listed.
   * The other auctions in the batch are listed sequentially from `first id` to `first id + count`.
   * @dev Notes:
   *   a) Approval should be granted for the NFTMarket contract before using this function.
   *   b) If any NFT is already listed for auction then the entire batch call will revert.
   */
  function batchListFromCollectionV3(
    address nftContract,
    uint256[] calldata tokenIds,
    uint256 worldOrExhibitionId,
    uint256 reservePrice,
    uint256 auctionDuration,
    bool shouldSetBuyPrice,
    uint256 buyPrice,
    uint256 saleStartsAt
  ) external returns (uint256 firstAuctionIdOfSequence) {
    firstAuctionIdOfSequence = batchListFromCollectionV2(
      nftContract,
      tokenIds,
      worldOrExhibitionId,
      reservePrice,
      auctionDuration,
      shouldSetBuyPrice,
      buyPrice
    );

    for (uint256 i = 0; i < tokenIds.length; ) {
      _setSaleStartsAtAndIgnoreErrorIfStartsAtAlreadySet(nftContract, tokenIds[i], saleStartsAt);

      unchecked {
        ++i;
      }
    }
  }

  /**
   * @notice Create reserve auction, set a buy price, sale start at time, and/or set exhibition for an NFT and escrow in
   * the market contract. This will check the current state of the auction, buy now, exhibition, and schedule, then
   * update the values if they have changed.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT from the collection to set prices for.
   * @param worldOrExhibitionId The World NFT or exhibition ID to associate the listings to.
   * Set this to 0 if n/a.
   * @param reservePrice The initial reserve price for the auction created.
   * Set the reservePrice to 0 to skip creating auction.
   * @param auctionDuration The duration of the auction created.
   * Set this to 0 to use the default duration of 24 hours.
   * Note: if the auction was previously created, the duration will be ignored (it will not update to the new duration).
   * @param shouldSetBuyPrice True if buy prices should be set for this NFT.
   * Set this to false to skip setting buy prices, or if there is an existing buy price it will cancel it.
   * 0 is a valid buy price enabling a giveaway.
   * @param buyPrice The price at which someone could buy this NFT.
   * @param saleStartsAt The time the sale should start at. 0 if the sale should start immediately.
   * @dev Notes:
   *   a) Approval should be granted for the NFTMarket contract before using this function.
   */
  function upsertListingV2(
    address nftContract,
    uint256 tokenId,
    uint256 worldOrExhibitionId,
    uint256 reservePrice,
    uint256 auctionDuration,
    bool shouldSetBuyPrice,
    uint256 buyPrice,
    uint256 saleStartsAt
  ) external {
    if (!shouldSetBuyPrice && buyPrice != 0) {
      revert NFTMarketRouterList_Buy_Price_Set_But_Should_Set_Buy_Price_Is_False();
    }

    if (!shouldSetBuyPrice && reservePrice == 0) {
      if (worldOrExhibitionId != 0) {
        // Attempting to set an exhibition without any listings.
        revert NFTMarketRouterList_Exhibition_Id_Set_Without_Prices();
      }
      if (saleStartsAt != 0) {
        // If not listed, schedule is irrelevant.
        revert NFTMarketRouterList_Schedule_Set_Without_Prices();
      }
    }

    // Attempting to cancel auction listing and update auction duration.
    if (reservePrice == 0 && auctionDuration != 0) {
      revert NFTMarketRouterList_Duration_Set_Without_Reserve_Price();
    }

    uint256 auctionId = INFTMarketGetters(nftMarket).getReserveAuctionIdFor(nftContract, tokenId);
    uint256 currentReservePrice;
    if (auctionId != 0) {
      currentReservePrice = INFTMarketGetters(nftMarket).getReserveAuction(auctionId).amount;
    }
    (address buyPriceSeller, uint256 currentBuyPrice) = INFTMarketGetters(nftMarket).getBuyPrice(nftContract, tokenId);

    // Attempting to set no listings for the NFT when none exist.
    if ((currentReservePrice == 0 && reservePrice == 0) && (buyPriceSeller == address(0) && !shouldSetBuyPrice)) {
      revert NFTMarketRouterList_Must_Set_Reserve_Or_Buy_Price();
    }

    // Attempting to keep the prices the same as they currently are.
    if (
      currentReservePrice == reservePrice &&
      ((!shouldSetBuyPrice && buyPriceSeller == address(0)) || (currentBuyPrice == buyPrice))
    ) {
      uint256 currentExhibitionId = INFTMarketGetters(nftMarket).getExhibitionIdForNft(nftContract, tokenId);
      uint256 currentSaleStartsAt = INFTMarketGetters(nftMarket).getSaleStartsAt(nftContract, tokenId);

      bool updated;
      // Attempting to keep the exhibition the same as it is.
      if (currentExhibitionId != worldOrExhibitionId) {
        _updateNftExhibition(nftContract, tokenId, worldOrExhibitionId);
        updated = true;
      }
      if (currentSaleStartsAt != saleStartsAt) {
        _setSaleStartsAt(nftContract, tokenId, saleStartsAt);
        updated = true;
      }

      if (!updated) {
        revert NFTMarketRouterList_Must_Set_Reserve_Buy_Price_Or_Exhibition();
      }
    } else {
      // Attempting to update at least one price.

      // Attempting to set a reserve price.
      if (reservePrice != 0) {
        // There is an already existing auction.
        if (currentReservePrice != 0) {
          // The current reserve price does not match the new reserve price,
          // so update the already existing auction.
          if (currentReservePrice != reservePrice) {
            _updateReserveAuctionV2(auctionId, worldOrExhibitionId, reservePrice);
          }

          // It may be nice to revert if the specified auction duration does not match the current auction duration,
          // until we are able to change durations. (alt cancel and recreate the auction)
        } else {
          // There is no auction currently in place, so create one.
          _createReserveAuctionV3(nftContract, tokenId, worldOrExhibitionId, reservePrice, auctionDuration);
        }
      }

      // Attempting to set a buy now price
      if (shouldSetBuyPrice) {
        // Only set a new buy now price if it does not match the current price.
        if (currentBuyPrice != buyPrice) {
          _setBuyPriceV2(nftContract, tokenId, worldOrExhibitionId, buyPrice);
        }
      }

      if (shouldSetBuyPrice || reservePrice != 0) {
        uint256 currentSaleStartsAt = INFTMarketGetters(nftMarket).getSaleStartsAt(nftContract, tokenId);
        if (currentSaleStartsAt != saleStartsAt) {
          _setSaleStartsAt(nftContract, tokenId, saleStartsAt);
        }
      }

      // Cancel reserve or buy prices
      // We do this at the end of the function so that we keep the item in escrow until all prices are set

      // Attempting to set no reserve price
      // If there is an existing auction, cancel it.
      if (reservePrice == 0 && currentReservePrice != 0) {
        _cancelReserveAuction(auctionId);
      }

      // Attempting to set no buy now price
      // If there is an existing buy now, cancel it.
      if (!shouldSetBuyPrice && buyPriceSeller != address(0)) {
        _cancelBuyPrice(nftContract, tokenId);
      }
    }
  }
}

File 38 of 50 : NFTMarketRouterWorldsNftUserRoles.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "./apis/WorldsNftRouterAPIs.sol";

error NFTMarketRouterWorldsNftUserRoles_No_User_Role_Actions();

/**
 * @title Offers value-added functions for managing user roles in the Worlds contract.
 * @author reggieag
 */
abstract contract NFTMarketRouterWorldsNftUserRoles is WorldsNftRouterAPIs {
  /// @notice The action to take for a user role.
  enum RoleAction {
    RevokeAllRoles,
    SetAdmin,
    SetEditor
  }

  /// @notice A user and the role action to take for that user.
  struct UserRoleAction {
    address user;
    RoleAction roleAction;
  }

  /**
   * Manages user roles for a world in bulk.
   * The caller can set an editor role, or an admin role, or revoke all roles for a user.
   * The `RoleAction` enum is used to specify the action to take for each user.
   * @param worldId The address of the NFT contract.
   * @param userRoleActions The user addresses and the role actions to take for that user.
   * @dev Notes:
   *   a) The caller of this function must have an admin role or be the owner of the world.
   *   b) Cannot set a role for a user if they already have that same role granted. For example, cannot set
   *      an editor role for a user if they already have an editor role.
   */
  function manageWorldRolesForUsers(uint256 worldId, UserRoleAction[] calldata userRoleActions) external {
    if (userRoleActions.length == 0) {
      revert NFTMarketRouterWorldsNftUserRoles_No_User_Role_Actions();
    }

    for (uint256 i = 0; i < userRoleActions.length; ) {
      if (userRoleActions[i].roleAction == RoleAction.RevokeAllRoles) {
        _revokeAllRolesForUser(worldId, userRoleActions[i].user);
      } else if (userRoleActions[i].roleAction == RoleAction.SetAdmin) {
        _setAdminRole(worldId, userRoleActions[i].user);
      } else {
        // if (userRoleActions[i].roleAction == RoleAction.SetEditor)
        _setEditorRole(worldId, userRoleActions[i].user);
      }

      unchecked {
        ++i;
      }
    }
  }
}

File 39 of 50 : Constants.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

/// Constant values shared across mixins.

/**
 * @dev 100% in basis points.
 */
uint256 constant BASIS_POINTS = 10_000;

/**
 * @dev The default admin role defined by OZ ACL modules.
 */
bytes32 constant DEFAULT_ADMIN_ROLE = 0x00;

////////////////////////////////////////////////////////////////
// Royalties & Take Rates
////////////////////////////////////////////////////////////////

/**
 * @dev The max take rate an exhibition can have.
 */
uint256 constant MAX_WORLD_TAKE_RATE = 5_000;

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

/**
 * @dev Default royalty cut paid out on secondary sales.
 * Set to 10% of the secondary sale.
 */
uint96 constant ROYALTY_IN_BASIS_POINTS = 1_000;

/**
 * @dev Reward paid to referrers when a sale is made.
 * Set to 1% of the sale amount. This 1% is deducted from the protocol fee.
 */
uint96 constant BUY_REFERRER_IN_BASIS_POINTS = 100;

/**
 * @dev 10%, expressed as a denominator for more efficient calculations.
 */
uint256 constant ROYALTY_RATIO = BASIS_POINTS / ROYALTY_IN_BASIS_POINTS;

/**
 * @dev 1%, expressed as a denominator for more efficient calculations.
 */
uint256 constant BUY_REFERRER_RATIO = BASIS_POINTS / BUY_REFERRER_IN_BASIS_POINTS;

////////////////////////////////////////////////////////////////
// Gas Limits
////////////////////////////////////////////////////////////////

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

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

/**
 * @dev The gas limit to send ETH to a single recipient, enough for a contract with a simple receiver.
 */
uint256 constant SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT = 20_000;

////////////////////////////////////////////////////////////////
// Collection Type Names
////////////////////////////////////////////////////////////////

/**
 * @dev The NFT collection type.
 */
string constant NFT_COLLECTION_TYPE = "NFT Collection";

/**
 * @dev The NFT drop collection type.
 */
string constant NFT_DROP_COLLECTION_TYPE = "NFT Drop Collection";

/**
 * @dev The NFT timed edition collection type.
 */
string constant NFT_TIMED_EDITION_COLLECTION_TYPE = "NFT Timed Edition Collection";

/**
 * @dev The NFT limited edition collection type.
 */
string constant NFT_LIMITED_EDITION_COLLECTION_TYPE = "NFT Limited Edition Collection";

////////////////////////////////////////////////////////////////
// Business Logic
////////////////////////////////////////////////////////////////

/**
 * @dev Limits scheduled start/end times to be less than 2 years in the future.
 */
uint256 constant MAX_SCHEDULED_TIME_IN_THE_FUTURE = 365 days * 2;

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

/**
 * @dev Protocol fee for edition mints in basis points.
 */
uint256 constant EDITION_PROTOCOL_FEE_IN_BASIS_POINTS = 500;

/**
 * @dev Hash of the edition type names.
 * This is precalculated in order to save gas on use.
 * `keccak256(abi.encodePacked(NFT_TIMED_EDITION_COLLECTION_TYPE))`
 */
bytes32 constant timedEditionTypeHash = 0xee2afa3f960e108aca17013728aafa363a0f4485661d9b6f41c6b4ddb55008ee;

bytes32 constant limitedEditionTypeHash = 0x7df1f68d01ab1a6ee0448a4c3fbda832177331ff72c471b12b0051c96742eef5;

File 40 of 50 : FETHNode.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

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

error FETHNode_FETH_Address_Is_Not_A_Contract();
error FETHNode_Only_FETH_Can_Transfer_ETH();

/**
 * @title A mixin for interacting with the FETH contract.
 * @author batu-inal & HardlyDifficult
 */
abstract contract FETHNode is ContextUpgradeable {
  using AddressUpgradeable for address;
  using AddressUpgradeable for address payable;

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

  constructor(address _feth) {
    if (!_feth.isContract()) {
      revert FETHNode_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 FETHNode_Only_FETH_Can_Transfer_ETH();
    }
  }

  /**
   * @notice Withdraw the msg.sender's available FETH balance if they requested more than the msg.value provided.
   * @dev This may revert if the msg.sender is non-receivable.
   * This helper should not be used anywhere that may lead to locked assets.
   * @param totalAmount The total amount of ETH required (including the msg.value).
   * @param shouldRefundSurplus If true, refund msg.value - totalAmount to the msg.sender.
   */
  function _tryUseFETHBalance(uint256 totalAmount, bool shouldRefundSurplus) internal {
    if (totalAmount > msg.value) {
      // Withdraw additional ETH required from the user's available FETH balance.
      unchecked {
        // The if above ensures delta will not underflow.
        // Withdraw ETH from the user's account in the FETH token contract,
        // making the funds available in this contract as ETH.
        feth.marketWithdrawFrom(_msgSender(), totalAmount - msg.value);
      }
    } else if (shouldRefundSurplus && totalAmount < msg.value) {
      // Return any surplus ETH to the user.
      unchecked {
        // The if above ensures this will not underflow
        payable(_msgSender()).sendValue(msg.value - totalAmount);
      }
    }
  }

  /**
   * @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);
  }
}

File 41 of 50 : FoundationTreasuryNodeV1.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

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

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

/**
 * @title Stores a reference to Foundation's treasury contract for other mixins to leverage.
 * @notice The treasury collects fees and defines admin/operator roles.
 * @author batu-inal & HardlyDifficult
 */
abstract contract FoundationTreasuryNodeV1 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) {
    treasuryAddress = 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
   * @dev This mixin uses a total of 2,001 slots.
   */
  uint256[2_000] private __gap;
}

File 42 of 50 : MarketFees.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

import "../../interfaces/internal/INFTCollectionType.sol";
import "../../interfaces/internal/IMarketUtils.sol";

import "./Constants.sol";
import "./FoundationTreasuryNodeV1.sol";
import "./MarketSharedCore.sol";
import "./MarketStructs.sol";
import "./SendValueWithFallbackWithdraw.sol";

error NFTMarketFees_Market_Utils_Is_Not_A_Contract();
error NFTMarketFees_Invalid_Protocol_Fee();

/**
 * @title A mixin to distribute funds when an NFT is sold.
 * @author batu-inal & HardlyDifficult
 */
abstract contract MarketFees is
  FoundationTreasuryNodeV1,
  ContextUpgradeable,
  MarketSharedCore,
  SendValueWithFallbackWithdraw
{
  using AddressUpgradeable for address;

  /**
   * @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 True for the Drop market which only performs primary sales. False if primary & secondary are supported.
  bool private immutable assumePrimarySale;

  /// @notice The fee collected by Foundation for sales facilitated by this market contract.
  uint256 private immutable defaultProtocolFeeInBasisPoints;

  /// @notice Reference to our MarketUtils contract.
  IMarketUtils private immutable marketUtils;

  /**
   * @notice Emitted when an 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 buyReferrerFee 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 buyReferrerFee,
    uint256 buyReferrerSellerFee
  );

  /**
   * @notice Emitted when an NFT is sold when associated with a sell referrer.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @param sellerReferrer The account which received the sell referral incentive.
   * @param sellerReferrerFee The portion of the seller revenue collected by the sell referrer.
   */
  event SellerReferralPaid(
    address indexed nftContract,
    uint256 indexed tokenId,
    address sellerReferrer,
    uint256 sellerReferrerFee
  );

  /**
   * @notice Sets the immutable variables for this contract.
   * @param _defaultProtocolFeeInBasisPoints The default protocol fee to use for this market.
   * @param marketUtilsAddress The address to use for our MarketUtils contract.
   * @param _assumePrimarySale True for the Drop market which only performs primary sales.
   * False if primary & secondary are supported.
   */
  constructor(uint16 _defaultProtocolFeeInBasisPoints, address marketUtilsAddress, bool _assumePrimarySale) {
    if (
      _defaultProtocolFeeInBasisPoints < BASIS_POINTS / BUY_REFERRER_RATIO ||
      _defaultProtocolFeeInBasisPoints + BASIS_POINTS / ROYALTY_RATIO >= BASIS_POINTS - MAX_WORLD_TAKE_RATE
    ) {
      /* If the protocol fee is invalid, revert:
       * Protocol fee must be greater than the buy referrer fee since referrer fees are deducted from the protocol fee.
       * The protocol fee must leave room for the creator royalties and the max exhibition take rate.
       */
      revert NFTMarketFees_Invalid_Protocol_Fee();
    }

    if (!marketUtilsAddress.isContract()) {
      revert NFTMarketFees_Market_Utils_Is_Not_A_Contract();
    }

    assumePrimarySale = _assumePrimarySale;
    defaultProtocolFeeInBasisPoints = _defaultProtocolFeeInBasisPoints;
    marketUtils = IMarketUtils(marketUtilsAddress);
  }

  /**
   * @notice Distributes funds to foundation, creator recipients, and NFT owner after a sale.
   * @dev `virtual` allows other mixins to be notified anytime a sale occurs.
   */
  function _distributeFunds(
    address nftContract,
    uint256 tokenId,
    address payable seller,
    uint256 price,
    address payable buyReferrer,
    address payable sellerReferrerPaymentAddress,
    uint16 sellerReferrerTakeRateInBasisPoints
  ) internal virtual returns (uint256 totalFees, uint256 creatorRev, uint256 sellerRev) {
    if (price == 0) {
      // When the sale price is 0, there are no revenue to distribute.
      return (0, 0, 0);
    }

    address payable[] memory creatorRecipients;
    uint256[] memory creatorShares;

    uint256 buyReferrerFee;
    uint256 sellerReferrerFee;
    (totalFees, creatorRecipients, creatorShares, sellerRev, buyReferrerFee, sellerReferrerFee) = getFees(
      nftContract,
      tokenId,
      seller,
      price,
      buyReferrer,
      sellerReferrerTakeRateInBasisPoints
    );

    // Pay the creator(s)
    // If just a single recipient was defined, use a larger gas limit in order to support in-contract split logic.
    uint256 creatorGasLimit = creatorRecipients.length == 1
      ? SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS
      : SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT;
    unchecked {
      for (uint256 i = 0; i < creatorRecipients.length; ++i) {
        _sendValueWithFallbackWithdraw(creatorRecipients[i], creatorShares[i], creatorGasLimit);
        // Sum the total creator rev from shares
        // creatorShares is in ETH so creatorRev will not overflow here.
        creatorRev += creatorShares[i];
      }
    }

    // Pay the seller
    _sendValueWithFallbackWithdraw(seller, sellerRev, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT);

    // Pay the protocol fee
    _sendValueWithFallbackWithdraw(getFoundationTreasury(), totalFees, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT);

    // Pay the buy referrer fee
    if (buyReferrerFee != 0) {
      _sendValueWithFallbackWithdraw(buyReferrer, buyReferrerFee, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT);
      emit BuyReferralPaid({
        nftContract: nftContract,
        tokenId: tokenId,
        buyReferrer: buyReferrer,
        buyReferrerFee: buyReferrerFee,
        buyReferrerSellerFee: 0
      });
      unchecked {
        // Add the referrer fee back into the total fees so that all 3 return fields sum to the total price for events
        totalFees += buyReferrerFee;
      }
    }

    if (sellerReferrerPaymentAddress != address(0)) {
      if (sellerReferrerFee != 0) {
        // Add the seller referrer fee back to revenue so that all 3 return fields sum to the total price for events.
        unchecked {
          if (sellerRev == 0) {
            // When sellerRev is 0, this is a primary sale and all revenue is attributed to the "creator".
            creatorRev += sellerReferrerFee;
          } else {
            sellerRev += sellerReferrerFee;
          }
        }

        _sendValueWithFallbackWithdraw(
          sellerReferrerPaymentAddress,
          sellerReferrerFee,
          SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT
        );
      }

      emit SellerReferralPaid(nftContract, tokenId, sellerReferrerPaymentAddress, sellerReferrerFee);
    }
  }

  /**
   * @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 payable buyReferrer,
    uint16 sellerReferrerTakeRateInBasisPoints
  )
    public
    view
    returns (
      uint256 protocolFeeAmount,
      address payable[] memory creatorRecipients,
      uint256[] memory creatorShares,
      uint256 sellerRev,
      uint256 buyReferrerFee,
      uint256 sellerReferrerFee
    )
  {
    MarketTransactionOptions memory options = MarketTransactionOptions({
      // Market info
      marketTakeRateInBasisPoints: _getProtocolFee(nftContract),
      assumePrimarySale: assumePrimarySale,
      // Sale info
      nftContract: nftContract,
      tokenId: tokenId,
      price: price,
      seller: seller,
      // Referrals
      buyReferrer: buyReferrer,
      sellerReferrerTakeRateInBasisPoints: sellerReferrerTakeRateInBasisPoints,
      // Transaction info
      sender: _msgSender()
    });

    (protocolFeeAmount, creatorRecipients, creatorShares, sellerRev, buyReferrerFee, sellerReferrerFee) = marketUtils
      .getTransactionBreakdown(options);
  }

  /**
   * @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 totalFees How much will be sent to the Foundation treasury and/or referrals.
   * @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 seller The address of the owner of the NFT.
   * If `sellerRev` is 0, this may be `address(0)`.
   * @dev Currently in use by the FNDMiddleware `getFees` call (now deprecated).
   */
  function getFeesAndRecipients(
    address nftContract,
    uint256 tokenId,
    uint256 price
  )
    external
    view
    returns (
      uint256 totalFees,
      uint256 creatorRev,
      address payable[] memory creatorRecipients,
      uint256[] memory creatorShares,
      uint256 sellerRev,
      address payable seller
    )
  {
    seller = _getSellerOrOwnerOf(nftContract, tokenId);
    (totalFees, creatorRecipients, creatorShares, sellerRev, , ) = getFees({
      nftContract: nftContract,
      tokenId: tokenId,
      seller: seller,
      price: price,
      // Notice: Setting this value is a breaking change for the FNDMiddleware contract.
      // Will be wired in an upcoming release to communicate the buy referral information.
      buyReferrer: payable(0),
      sellerReferrerTakeRateInBasisPoints: 0
    });

    // Sum the total creator rev from shares
    unchecked {
      for (uint256 i = 0; i < creatorShares.length; ++i) {
        creatorRev += creatorShares[i];
      }
    }
  }

  /**
   * @notice returns the address of the MarketUtils contract.
   */
  function getMarketUtilsAddress() external view returns (address marketUtilsAddress) {
    marketUtilsAddress = address(marketUtils);
  }

  /**
   * @notice Calculates the protocol fee for the given NFT contract.
   * @dev This returns the contract's default fee but may be overridden to change fees based on the collection type.
   */
  function _getProtocolFee(address /* nftContract */) internal view virtual returns (uint256 protocolFeeInBasisPoints) {
    protocolFeeInBasisPoints = defaultProtocolFeeInBasisPoints;
  }

  /**
   * @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 This mixins uses 504 slots in total.
   */
  uint256[500] private __gap;
}

File 43 of 50 : MarketSharedCore.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "./FETHNode.sol";

/**
 * @title A place for common modifiers and functions used by various market 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.
 * @author batu-inal & HardlyDifficult
 */
abstract contract MarketSharedCore is FETHNode {
  /**
   * @notice Checks who the seller for an NFT is if listed in this market.
   * @param nftContract The address of the NFT contract.
   * @param tokenId The id of the NFT.
   * @return seller The seller which listed this NFT for sale, or address(0) if not listed.
   */
  function getSellerOf(address nftContract, uint256 tokenId) external view returns (address payable seller) {
    seller = _getSellerOf(nftContract, tokenId);
  }

  /**
   * @notice Checks who the seller for an NFT is if listed in this market.
   */
  function _getSellerOf(address nftContract, uint256 tokenId) internal view virtual returns (address payable seller) {
    // Returns address(0) by default.
  }

  /**
   * @notice Checks who the seller for an NFT is if listed in this market or returns the current owner.
   */
  function _getSellerOrOwnerOf(
    address nftContract,
    uint256 tokenId
  ) internal view virtual returns (address payable sellerOrOwner);

  /**
   * @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[450] private __gap;
}

File 44 of 50 : MarketStructs.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

/// @notice Details about a marketplace sale.
struct MarketTransactionOptions {
  ////////////////////////////////////////////////////////////////
  // Market config
  ////////////////////////////////////////////////////////////////
  /// @notice Percentage of the transaction to go the the market, expressed in basis points.
  uint256 marketTakeRateInBasisPoints;
  /// @notice set to true when the token is being sold by it's creator
  bool assumePrimarySale;
  ////////////////////////////////////////////////////////////////
  // Sale info
  ////////////////////////////////////////////////////////////////
  /// @notice The contract address of the nft
  address nftContract;
  /// @notice The token id of the nft.
  uint256 tokenId;
  /// @notice price at which the token is being sold
  uint256 price;
  /// @notice address of the account that is selling the token
  address payable seller;
  ////////////////////////////////////////////////////////////////
  // Referrals
  ////////////////////////////////////////////////////////////////
  /// @notice Address of the account that referred the buyer.
  address payable buyReferrer;
  /// @notice Percentage of the transaction to go the the account which referred the seller, expressed in basis points.
  uint16 sellerReferrerTakeRateInBasisPoints;
  ////////////////////////////////////////////////////////////////
  // Transaction info
  ////////////////////////////////////////////////////////////////
  /// @notice The msg.sender which executed the purchase transaction.
  address sender;
}

/// @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;
}

File 45 of 50 : NFTCollectionFactoryNode.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

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

error NFTCollectionFactoryNode_Address_Is_Not_A_Contract();

/**
 * @title Stores a reference to Foundation's NFTCollectionFactory contract for other mixins to leverage.
 * @author HardlyDifficult
 */
abstract contract NFTCollectionFactoryNode {
  using AddressUpgradeable for address;

  address internal immutable nftCollectionFactory;

  constructor(address _nftCollectionFactory) {
    if (!_nftCollectionFactory.isContract()) {
      revert NFTCollectionFactoryNode_Address_Is_Not_A_Contract();
    }

    nftCollectionFactory = _nftCollectionFactory;
  }

  /**
   * @notice Returns the address of the NFTMarket contract.
   */
  function getNftCollectionFactoryAddress() external view returns (address factory) {
    factory = nftCollectionFactory;
  }

  // This mixin uses 0 slots.
}

File 46 of 50 : NFTDropMarketNode.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

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

/**
 * @title Stores a reference to Foundation's NFTDropMarket contract for other mixins to leverage.
 * @author HardlyDifficult
 */
abstract contract NFTDropMarketNode {
  using AddressUpgradeable for address;

  address internal immutable nftDropMarket;

  error NFTDropMarketNode_Address_Is_Not_A_Contract();

  constructor(address _nftDropMarket) {
    if (!_nftDropMarket.isContract()) {
      revert NFTDropMarketNode_Address_Is_Not_A_Contract();
    }

    nftDropMarket = _nftDropMarket;
  }

  /**
   * @notice Returns the address of the NFTDropMarket contract.
   */
  function getNftDropMarketAddress() external view returns (address market) {
    market = nftDropMarket;
  }

  // This mixin uses 0 slots.
}

File 47 of 50 : NFTMarketNode.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

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

/**
 * @title Stores a reference to Foundation's NFTMarket contract for other mixins to leverage.
 * @author HardlyDifficult
 */
abstract contract NFTMarketNode {
  using AddressUpgradeable for address;

  address internal immutable nftMarket;

  error NFTMarketNode_Address_Is_Not_A_Contract();

  constructor(address _nftMarket) {
    if (!_nftMarket.isContract()) {
      revert NFTMarketNode_Address_Is_Not_A_Contract();
    }

    nftMarket = _nftMarket;
  }

  /**
   * @notice Returns the address of the NFTMarket contract.
   */
  function getNftMarketAddress() external view returns (address market) {
    market = nftMarket;
  }

  // This mixin uses 0 slots.
}

File 48 of 50 : SendValueWithFallbackWithdraw.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

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

import "./FETHNode.sol";
import "./FoundationTreasuryNodeV1.sol";

/**
 * @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.
 * @author batu-inal & HardlyDifficult
 */
abstract contract SendValueWithFallbackWithdraw is FoundationTreasuryNodeV1, FETHNode {
  using AddressUpgradeable for address payable;

  /// @dev Removing old unused variables in an upgrade safe way.
  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);

  /**
   * @notice Attempt to send a user or contract ETH.
   * If it fails store the amount owned for later withdrawal in FETH.
   * @dev This may fail when sending ETH to a contract that is non-receivable or exceeds the gas limit specified.
   */
  function _sendValueWithFallbackWithdraw(address payable user, uint256 amount, uint256 gasLimit) internal {
    if (amount == 0) {
      return;
    }

    if (user == address(feth)) {
      // FETH may revert on ETH transfers and will reject `depositFor` calls to itself, so redirect funds to the
      // treasury contract instead.
      user = getFoundationTreasury();
    }

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

  /**
   * @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[999] private __gap;
}

File 49 of 50 : TxDeadline.sol
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.18;

import "../../libraries/TimeLibrary.sol";

error TxDeadline_Tx_Deadline_Expired();

/**
 * @title A mixin that provides a modifier to check that a transaction deadline has not expired.
 * @author HardlyDifficult
 */
abstract contract TxDeadline {
  using TimeLibrary for uint256;

  /// @notice Requires the deadline provided is 0, now, or in the future.
  modifier txDeadlineNotExpired(uint256 txDeadlineTime) {
    // No transaction deadline when set to 0.
    if (txDeadlineTime != 0 && txDeadlineTime.hasExpired()) {
      revert TxDeadline_Tx_Deadline_Expired();
    }
    _;
  }

  // This mixin does not use any storage.
}

File 50 of 50 : WorldsNftNode.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.18;

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

error WorldsNftNode_Worlds_NFT_Is_Not_A_Contract();

/**
 * @title Stores a reference to the Worlds NFT contract for contracts to leverage.
 * @author HardlyDifficult
 */
abstract contract WorldsNftNode {
  using AddressUpgradeable for address;

  address internal immutable worlds;

  constructor(address worldsNft) {
    if (!worldsNft.isContract()) {
      revert WorldsNftNode_Worlds_NFT_Is_Not_A_Contract();
    }

    worlds = worldsNft;
  }

  /**
   * @notice Returns the address of the Worlds NFT contract.
   */
  function getWorldsNftAddress() external view returns (address worldsNft) {
    worldsNft = worlds;
  }

  // This mixin uses 0 slots.
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_nftMarket","type":"address"},{"internalType":"address","name":"_nftDropMarket","type":"address"},{"internalType":"address","name":"_nftCollectionFactory","type":"address"},{"internalType":"address","name":"_worlds","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NFTCollectionFactoryNode_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTCreateAndListTimedEditionCollection_Sale_Duration_Cannot_Be_Zero","type":"error"},{"inputs":[],"name":"NFTDropMarketNode_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTMarketNode_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTMarketRouterList_Buy_Price_Set_But_Should_Set_Buy_Price_Is_False","type":"error"},{"inputs":[],"name":"NFTMarketRouterList_Duration_Set_Without_Reserve_Price","type":"error"},{"inputs":[],"name":"NFTMarketRouterList_Exhibition_Id_Set_Without_Prices","type":"error"},{"inputs":[],"name":"NFTMarketRouterList_Must_Set_Reserve_Buy_Price_Or_Exhibition","type":"error"},{"inputs":[],"name":"NFTMarketRouterList_Must_Set_Reserve_Or_Buy_Price","type":"error"},{"inputs":[],"name":"NFTMarketRouterList_Schedule_Set_Without_Prices","type":"error"},{"inputs":[],"name":"NFTMarketRouterList_Token_Ids_Not_Set","type":"error"},{"inputs":[],"name":"NFTMarketRouterWorldsNftUserRoles_No_User_Role_Actions","type":"error"},{"inputs":[],"name":"RouteCallLibrary_Call_Failed_Without_Revert_Reason","type":"error"},{"inputs":[],"name":"TxDeadline_Tx_Deadline_Expired","type":"error"},{"inputs":[],"name":"WorldsNftNode_Worlds_NFT_Is_Not_A_Contract","type":"error"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"},{"internalType":"uint256","name":"auctionDuration","type":"uint256"},{"internalType":"bool","name":"shouldSetBuyPrice","type":"bool"},{"internalType":"uint256","name":"buyPrice","type":"uint256"}],"name":"batchListFromCollectionV2","outputs":[{"internalType":"uint256","name":"firstAuctionIdOfSequence","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"},{"internalType":"uint256","name":"auctionDuration","type":"uint256"},{"internalType":"bool","name":"shouldSetBuyPrice","type":"bool"},{"internalType":"uint256","name":"buyPrice","type":"uint256"},{"internalType":"uint256","name":"saleStartsAt","type":"uint256"}],"name":"batchListFromCollectionV3","outputs":[{"internalType":"uint256","name":"firstAuctionIdOfSequence","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"components":[{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"}],"internalType":"struct FixedPriceSaleParams","name":"fixedPriceSaleParams","type":"tuple"},{"internalType":"uint256","name":"txDeadlineTime","type":"uint256"}],"name":"createFixedPriceSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"components":[{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"}],"internalType":"struct FixedPriceSaleParams","name":"fixedPriceSaleParams","type":"tuple"},{"internalType":"uint256","name":"earlyAccessStartTime","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"string","name":"merkleTreeUri","type":"string"},{"internalType":"uint256","name":"txDeadlineTime","type":"uint256"}],"name":"createFixedPriceSaleWithEarlyAccessAllowlist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"tokenURI","type":"string"},{"internalType":"uint32","name":"maxTokenId","type":"uint32"},{"internalType":"uint96","name":"nonce","type":"uint96"}],"internalType":"struct LimitedEditionCollectionCreationParams","name":"collectionParams","type":"tuple"},{"components":[{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"}],"internalType":"struct FixedPriceSaleParams","name":"fixedPriceSaleParams","type":"tuple"},{"internalType":"uint256","name":"txDeadlineTime","type":"uint256"}],"name":"createLimitedEditionCollectionAndFixedPriceSale","outputs":[{"internalType":"address","name":"collection","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"tokenURI","type":"string"},{"internalType":"uint32","name":"maxTokenId","type":"uint32"},{"internalType":"uint96","name":"nonce","type":"uint96"}],"internalType":"struct LimitedEditionCollectionCreationParams","name":"collectionParams","type":"tuple"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct CallWithoutValue","name":"paymentAddressFactoryCall","type":"tuple"},{"components":[{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"}],"internalType":"struct FixedPriceSaleParams","name":"fixedPriceSaleParams","type":"tuple"},{"internalType":"uint256","name":"txDeadlineTime","type":"uint256"}],"name":"createLimitedEditionCollectionAndFixedPriceSaleWithPaymentFactory","outputs":[{"internalType":"address","name":"collection","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"saleDuration","type":"uint256"}],"name":"createLinearDutchAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"tokenURI","type":"string"},{"internalType":"uint96","name":"nonce","type":"uint96"}],"internalType":"struct TimedEditionCollectionCreationParams","name":"collectionParams","type":"tuple"},{"components":[{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"},{"internalType":"uint256","name":"saleDuration","type":"uint256"}],"internalType":"struct FixedPriceSaleParamsV2","name":"fixedPriceSaleParams","type":"tuple"},{"internalType":"uint256","name":"txDeadlineTime","type":"uint256"}],"name":"createTimedEditionCollectionAndFixedPriceSaleV2","outputs":[{"internalType":"address","name":"collection","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"tokenURI","type":"string"},{"internalType":"uint96","name":"nonce","type":"uint96"}],"internalType":"struct TimedEditionCollectionCreationParams","name":"collectionParams","type":"tuple"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct CallWithoutValue","name":"paymentAddressFactoryCall","type":"tuple"},{"components":[{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"},{"internalType":"uint256","name":"saleDuration","type":"uint256"}],"internalType":"struct FixedPriceSaleParamsV2","name":"fixedPriceSaleParams","type":"tuple"},{"internalType":"uint256","name":"txDeadlineTime","type":"uint256"}],"name":"createTimedEditionCollectionAndFixedPriceSaleWithPaymentFactoryV2","outputs":[{"internalType":"address","name":"collection","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getNftCollectionFactoryAddress","outputs":[{"internalType":"address","name":"factory","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNftDropMarketAddress","outputs":[{"internalType":"address","name":"market","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNftMarketAddress","outputs":[{"internalType":"address","name":"market","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWorldsNftAddress","outputs":[{"internalType":"address","name":"worldsNft","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"worldId","type":"uint256"},{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"enum NFTMarketRouterWorldsNftUserRoles.RoleAction","name":"roleAction","type":"uint8"}],"internalType":"struct NFTMarketRouterWorldsNftUserRoles.UserRoleAction[]","name":"userRoleActions","type":"tuple[]"}],"name":"manageWorldRolesForUsers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"worldOrExhibitionId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"},{"internalType":"uint256","name":"auctionDuration","type":"uint256"},{"internalType":"bool","name":"shouldSetBuyPrice","type":"bool"},{"internalType":"uint256","name":"buyPrice","type":"uint256"},{"internalType":"uint256","name":"saleStartsAt","type":"uint256"}],"name":"upsertListingV2","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61010060405234801562000011575f80fd5b50604051620032b4380380620032b4833981016040819052620000349162000121565b808383866001600160a01b0381163b62000061576040516348dc361f60e01b815260040160405180910390fd5b6001600160a01b0390811660805281163b62000090576040516307fa343360e41b815260040160405180910390fd5b6001600160a01b0390811660a05281163b620000bf576040516376d10ccd60e11b815260040160405180910390fd5b6001600160a01b0390811660c05281163b620000ee57604051631341312360e01b815260040160405180910390fd5b6001600160a01b031660e052506200017b92505050565b80516001600160a01b03811681146200011c575f80fd5b919050565b5f805f806080858703121562000135575f80fd5b620001408562000105565b9350620001506020860162000105565b9250620001606040860162000105565b9150620001706060860162000105565b905092959194509250565b60805160a05160c05160e051613041620002735f395f818161018b015281816114bc01528181611589015261165601525f818161023601528181610e5201528181610fa301528181611c2801528181611dbb01528181611ee301528181611fe1015261211f01525f818161012f01528181611aeb01528181611cd001528181611f8c01526120ca01525f81816101b10152818161078f015281816108320152818161090801528181610a6a01528181610b2601528181610ca60152818161119c015281816112f3015281816113ef0152818161172a015281816117fe015281816118bd015281816119630152611a4301526130415ff3fe608060405234801561000f575f80fd5b50600436106100f0575f3560e01c80635599dfd711610093578063b590104e11610063578063b590104e14610234578063bfec346c1461025a578063cf2b5aa61461026d578063dbe3352a14610280575f80fd5b80635599dfd7146101e857806365c2cd91146101fb5780637e32d2411461020e5780637f9d22fe14610221575f80fd5b8063225cd1eb116100ce578063225cd1eb14610174578063228b131814610189578063331aaf9e146101af578063353ad881146101d5575f80fd5b80630a3ec799146100f45780631b39f9151461011a5780632059d9e21461012d575b5f80fd5b610107610102366004612399565b610293565b6040519081526020015b60405180910390f35b610107610128366004612428565b6102e9565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610111565b6101876101823660046124ae565b6104cc565b005b7f000000000000000000000000000000000000000000000000000000000000000061014f565b7f000000000000000000000000000000000000000000000000000000000000000061014f565b6101876101e3366004612526565b610639565b61014f6101f63660046125b6565b610d82565b61014f61020936600461261a565b610ed3565b61018761021c366004612693565b611026565b61018761022f366004612740565b61104c565b7f000000000000000000000000000000000000000000000000000000000000000061014f565b61014f610268366004612790565b61105b565b61014f61027b3660046127e4565b6110d3565b61018761028e36600461285c565b611147565b5f6102a48a8a8a8a8a8a8a8a6102e9565b90505f5b888110156102db576102d38b8b8b848181106102c6576102c6612888565b9050602002013585611165565b6001016102a8565b509998505050505050505050565b5f868103610323576040517fa9d004a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8215801561033057508115155b15610367576040517f1a8a2c1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8415610427576103928989895f81811061038357610383612888565b905060200201358888886112ae565b905082156103be576103be8989895f8181106103b0576103b0612888565b9050602002013588856113b5565b60015b87811015610421576103ee8a8a8a848181106103df576103df612888565b905060200201358989896112ae565b508315610419576104198a8a8a8481811061040b5761040b612888565b9050602002013589866113b5565b6001016103c1565b506104c0565b831561045f576040517fbf2c1df000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82610496576040517f75ce98b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b878110156104be576104b68a8a8a8481811061040b5761040b612888565b600101610498565b505b98975050505050505050565b5f819003610506576040517f589a99ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b81811015610633575f83838381811061052357610523612888565b905060400201602001602081019061053b91906128e2565b600281111561054c5761054c6128b5565b03610587576105828484848481811061056757610567612888565b61057d9260206040909202019081019150612900565b611490565b61062b565b600183838381811061059b5761059b612888565b90506040020160200160208101906105b391906128e2565b60028111156105c4576105c46128b5565b036105fa57610582848484848181106105df576105df612888565b6105f59260206040909202019081019150612900565b61155d565b61062b8484848481811061061057610610612888565b6106269260206040909202019081019150612900565b61162a565b600101610508565b50505050565b8215801561064657508115155b1561067d576040517f1a8a2c1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82158015610689575084155b156106fd5785156106c5576040517e99fb3000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80156106fd576040517f69a1d31600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8415801561070a57508315155b15610741576040517fbf2c1df000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f2ab2b52b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152602482018990525f917f000000000000000000000000000000000000000000000000000000000000000090911690632ab2b52b90604401602060405180830381865afa1580156107d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107fa919061291b565b90505f81156108b8576040517f9e79b41f000000000000000000000000000000000000000000000000000000008152600481018390527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639e79b41f9060240161010060405180830381865afa15801561088d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108b1919061293d565b60e0015190505b6040517f4635256e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b81166004830152602482018b90525f9182917f00000000000000000000000000000000000000000000000000000000000000001690634635256e906044016040805180830381865afa15801561094c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109709190612a0a565b91509150825f148015610981575088155b80156109aa575073ffffffffffffffffffffffffffffffffffffffff82161580156109aa575086155b156109e1576040517f75ce98b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8883148015610a17575086158015610a0d575073ffffffffffffffffffffffffffffffffffffffff8216155b80610a1757508581145b15610bfd576040517f442559a200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d81166004830152602482018d90525f917f00000000000000000000000000000000000000000000000000000000000000009091169063442559a290604401602060405180830381865afa158015610ab1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ad5919061291b565b6040517f7e04379500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8f81166004830152602482018f90529192505f917f00000000000000000000000000000000000000000000000000000000000000001690637e04379590604401602060405180830381865afa158015610b6b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b8f919061291b565b90505f8c8314610ba857610ba48f8f8f6116f7565b5060015b878214610bbe57610bba8f8f8a6117cb565b5060015b80610bf5576040517f41deec4500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050610d74565b8815610c2f578215610c2057888314610c1b57610c1b848b8b61189f565b610c2f565b610c2d8c8c8c8c8c6112ae565b505b8615610c4857858114610c4857610c488c8c8c896113b5565b8680610c5357508815155b15610d27576040517f7e04379500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d81166004830152602482018d90525f917f000000000000000000000000000000000000000000000000000000000000000090911690637e04379590604401602060405180830381865afa158015610ced573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d11919061291b565b9050858114610d2557610d258d8d886117cb565b505b88158015610d3457508215155b15610d4257610d428461195e565b86158015610d65575073ffffffffffffffffffffffffffffffffffffffff821615155b15610d7457610d748c8c611a17565b505050505050505050505050565b5f818015801590610d975750610d9781421190565b15610dce576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83608001355f03610e0b576040517fa87860d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401355f819003610e1b5750425b610e89610e288780612a36565b610e3560208a018a612a36565b610e4260408c018c612a36565b610e5060808d013589612a97565b7f00000000000000000000000000000000000000000000000000000000000000008e6060016020810190610e849190612ad5565b611ae4565b92505f6040518060800160405280875f01358152602001876020013581526020018760400135815260200187606001358152509050610ec984825f611bce565b5050509392505050565b5f818015801590610ee85750610ee881421190565b15610f1f576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83608001355f03610f5c576040517fa87860d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401355f819003610f6c5750425b610fdb610f798880612a36565b610f8660208b018b612a36565b610f9360408d018d612a36565b610fa160808d013589612a97565b7f00000000000000000000000000000000000000000000000000000000000000008f6060016020810190610fd59190612ad5565b8f611cc9565b92505f6040518060800160405280875f0135815260200187602001358152602001876040013581526020018760600135815250905061101b84825f611bce565b505050949350505050565b6110438761103936899003890189612b00565b8787878787611db6565b50505050505050565b61104387878787878787611e94565b5f818015801590611070575061107081421190565b156110a7576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110b085611f85565b91506110cb826110c536879003870187612b00565b5f611bce565b509392505050565b5f8180158015906110e857506110e881421190565b1561111f576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61112986866120c3565b915061113e826110c536879003870187612b00565b50949350505050565b6111608361115a36859003850185612b00565b83611bce565b505050565b60405173ffffffffffffffffffffffffffffffffffffffff8416602482015260448101839052606481018290525f90819061123d907f000000000000000000000000000000000000000000000000000000000000000090608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4eb1231700000000000000000000000000000000000000000000000000000000179052339190612207565b915091508115801561129957507fe5c3f2630000000000000000000000000000000000000000000000000000000061127482612b89565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b156112a7576112a7816122b7565b5050505050565b60405173ffffffffffffffffffffffffffffffffffffffff8616602482015260448101859052606481018490526084810183905260a481018290525f908190611394907f00000000000000000000000000000000000000000000000000000000000000009060c401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8098531d000000000000000000000000000000000000000000000000000000001790523391906122fc565b9050808060200190518101906113aa919061291b565b979650505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff851660248201526044810184905260648101839052608481018290526112a7907f00000000000000000000000000000000000000000000000000000000000000009060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2fbbb25a000000000000000000000000000000000000000000000000000000001790523391906122fc565b6040516024810183905273ffffffffffffffffffffffffffffffffffffffff82166044820152611160907f000000000000000000000000000000000000000000000000000000000000000090606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f05d8d4f2000000000000000000000000000000000000000000000000000000001790523391906122fc565b6040516024810183905273ffffffffffffffffffffffffffffffffffffffff82166044820152611160907f000000000000000000000000000000000000000000000000000000000000000090606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095f0c70000000000000000000000000000000000000000000000000000000001790523391906122fc565b6040516024810183905273ffffffffffffffffffffffffffffffffffffffff82166044820152611160907f000000000000000000000000000000000000000000000000000000000000000090606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f691aa51a000000000000000000000000000000000000000000000000000000001790523391906122fc565b60405173ffffffffffffffffffffffffffffffffffffffff841660248201526044810183905260648101829052610633907f000000000000000000000000000000000000000000000000000000000000000090608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f574c8229000000000000000000000000000000000000000000000000000000001790523391906122fc565b60405173ffffffffffffffffffffffffffffffffffffffff841660248201526044810183905260648101829052610633907f000000000000000000000000000000000000000000000000000000000000000090608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4eb12317000000000000000000000000000000000000000000000000000000001790523391906122fc565b604051602481018490526044810183905260648101829052610633907f000000000000000000000000000000000000000000000000000000000000000090608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe2cf968b000000000000000000000000000000000000000000000000000000001790523391906122fc565b611a137f00000000000000000000000000000000000000000000000000000000000000008260405160240161199591815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f21506fff000000000000000000000000000000000000000000000000000000001790523391906122fc565b5050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611160907f000000000000000000000000000000000000000000000000000000000000000090606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f21561935000000000000000000000000000000000000000000000000000000001790523391906122fc565b5f80611ba97f00000000000000000000000000000000000000000000000000000000000000008c8c8c8c8c8c8c8c8c604051602401611b2b99989796959493929190612c1f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f189267b2000000000000000000000000000000000000000000000000000000001790523391906122fc565b905080806020019051810190611bbf9190612ca0565b9b9a5050505050505050505050565b815160208301516040808501516060860151915173ffffffffffffffffffffffffffffffffffffffff8816602482015260448101949094526064840192909252608483019190915260a482015260c48101829052610633907f00000000000000000000000000000000000000000000000000000000000000009060e401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f42017634000000000000000000000000000000000000000000000000000000001790523391906122fc565b5f80611d907f00000000000000000000000000000000000000000000000000000000000000008d8d8d8d8d8d8d8d8d8d604051602401611d129a99989796959493929190612d5c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa973d0e8000000000000000000000000000000000000000000000000000000001790523391906122fc565b905080806020019051810190611da69190612ca0565b9c9b505050505050505050505050565b611e8a7f000000000000000000000000000000000000000000000000000000000000000088885f015189602001518a604001518b606001518b8b8b8b8b604051602401611e0c9a99989796959493929190612df2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9901261c000000000000000000000000000000000000000000000000000000001790523391906122fc565b5050505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff8816602482015260448101879052606481018690526084810185905260a4810184905260c4810183905260e48101829052611e8a907f00000000000000000000000000000000000000000000000000000000000000009061010401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f7f9d22fe000000000000000000000000000000000000000000000000000000001790523391906122fc565b5f806120a67f0000000000000000000000000000000000000000000000000000000000000000611fb58580612a36565b611fc26020880188612a36565b611fcf60408a018a612a36565b611fdf60808c0160608d01612e5f565b7f000000000000000000000000000000000000000000000000000000000000000061201060a08e0160808f01612ad5565b60405160240161202899989796959493929190612e82565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fd061a9f2000000000000000000000000000000000000000000000000000000001790523391906122fc565b9050808060200190518101906120bc9190612ca0565b9392505050565b5f806121e97f00000000000000000000000000000000000000000000000000000000000000006120f38680612a36565b6121006020890189612a36565b61210d60408b018b612a36565b61211d60808d0160608e01612e5f565b7f00000000000000000000000000000000000000000000000000000000000000008d60800160208101906121519190612ad5565b8d60405160240161216b9a99989796959493929190612f09565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f57848031000000000000000000000000000000000000000000000000000000001790523391906122fc565b9050808060200190518101906121ff9190612ca0565b949350505050565b5f60608373ffffffffffffffffffffffffffffffffffffffff168386604051602001612234929190612fc1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261226c91613000565b5f604051808303815f865af19150503d805f81146122a5576040519150601f19603f3d011682016040523d82523d5f602084013e6122aa565b606091505b5090969095509350505050565b8051156122c75780518082602001fd5b6040517f3cfe059f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b60605f61230a858585612207565b92509050806110cb576110cb826122b7565b73ffffffffffffffffffffffffffffffffffffffff811681146122f9575f80fd5b5f8083601f84011261234d575f80fd5b50813567ffffffffffffffff811115612364575f80fd5b6020830191508360208260051b850101111561237e575f80fd5b9250929050565b80358015158114612394575f80fd5b919050565b5f805f805f805f805f6101008a8c0312156123b2575f80fd5b89356123bd8161231c565b985060208a013567ffffffffffffffff8111156123d8575f80fd5b6123e48c828d0161233d565b90995097505060408a0135955060608a0135945060808a0135935061240b60a08b01612385565b925060c08a0135915060e08a013590509295985092959850929598565b5f805f805f805f8060e0898b03121561243f575f80fd5b883561244a8161231c565b9750602089013567ffffffffffffffff811115612465575f80fd5b6124718b828c0161233d565b90985096505060408901359450606089013593506080890135925061249860a08a01612385565b915060c089013590509295985092959890939650565b5f805f604084860312156124c0575f80fd5b83359250602084013567ffffffffffffffff808211156124de575f80fd5b818601915086601f8301126124f1575f80fd5b8135818111156124ff575f80fd5b8760208260061b8501011115612513575f80fd5b6020830194508093505050509250925092565b5f805f805f805f80610100898b03121561253e575f80fd5b88356125498161231c565b97506020890135965060408901359550606089013594506080890135935061257360a08a01612385565b925060c0890135915060e089013590509295985092959890939650565b5f608082840312156125a0575f80fd5b50919050565b5f60a082840312156125a0575f80fd5b5f805f60e084860312156125c8575f80fd5b833567ffffffffffffffff8111156125de575f80fd5b6125ea86828701612590565b9350506125fa85602086016125a6565b915060c084013590509250925092565b5f604082840312156125a0575f80fd5b5f805f80610100858703121561262e575f80fd5b843567ffffffffffffffff80821115612645575f80fd5b61265188838901612590565b95506020870135915080821115612666575f80fd5b506126738782880161260a565b93505061268386604087016125a6565b9396929550929360e00135925050565b5f805f805f805f610120888a0312156126aa575f80fd5b87356126b58161231c565b96506126c48960208a01612590565b955060a0880135945060c0880135935060e088013567ffffffffffffffff808211156126ee575f80fd5b818a0191508a601f830112612701575f80fd5b81358181111561270f575f80fd5b8b6020828501011115612720575f80fd5b602083019550809450505050610100880135905092959891949750929550565b5f805f805f805f60e0888a031215612756575f80fd5b87356127618161231c565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b5f805f60c084860312156127a2575f80fd5b833567ffffffffffffffff8111156127b8575f80fd5b6127c4868287016125a6565b9350506127d48560208601612590565b915060a084013590509250925092565b5f805f8060e085870312156127f7575f80fd5b843567ffffffffffffffff8082111561280e575f80fd5b61281a888389016125a6565b9550602087013591508082111561282f575f80fd5b5061283c8782880161260a565b93505061284c8660408701612590565b9396929550929360c00135925050565b5f805f60c0848603121561286e575f80fd5b83356128798161231c565b92506127d48560208601612590565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f602082840312156128f2575f80fd5b8135600381106120bc575f80fd5b5f60208284031215612910575f80fd5b81356120bc8161231c565b5f6020828403121561292b575f80fd5b5051919050565b80516123948161231c565b5f61010080838503121561294f575f80fd5b6040519081019067ffffffffffffffff82118183101715612997577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b81604052835191506129a88261231c565b818152602084015160208201526129c160408501612932565b6040820152606084015160608201526080840151608082015260a084015160a08201526129f060c08501612932565b60c082015260e084015160e0820152809250505092915050565b5f8060408385031215612a1b575f80fd5b8251612a268161231c565b6020939093015192949293505050565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612a69575f80fd5b83018035915067ffffffffffffffff821115612a83575f80fd5b60200191503681900382131561237e575f80fd5b80820180821115612acf577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b92915050565b5f60208284031215612ae5575f80fd5b81356bffffffffffffffffffffffff811681146120bc575f80fd5b5f60808284031215612b10575f80fd5b6040516080810181811067ffffffffffffffff82111715612b58577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b5f815160208301517fffffffff0000000000000000000000000000000000000000000000000000000080821693506004831015612bd05780818460040360031b1b83161693505b505050919050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60c081525f612c3260c083018b8d612bd8565b8281036020840152612c45818a8c612bd8565b90508281036040840152612c5a81888a612bd8565b6060840196909652505073ffffffffffffffffffffffffffffffffffffffff9290921660808301526bffffffffffffffffffffffff1660a0909101529695505050505050565b5f60208284031215612cb0575f80fd5b81516120bc8161231c565b5f8135612cc78161231c565b73ffffffffffffffffffffffffffffffffffffffff1683526020820135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1018112612d14575f80fd5b820160208101903567ffffffffffffffff811115612d30575f80fd5b803603821315612d3e575f80fd5b60406020860152612d53604086018284612bd8565b95945050505050565b60e081525f612d6f60e083018c8e612bd8565b8281036020840152612d82818b8d612bd8565b90508281036040840152612d9781898b612bd8565b905086606084015273ffffffffffffffffffffffffffffffffffffffff861660808401526bffffffffffffffffffffffff851660a084015282810360c0840152612de18185612cbb565b9d9c50505050505050505050505050565b5f61012073ffffffffffffffffffffffffffffffffffffffff8d1683528b60208401528a60408401528960608401528860808401528760a08401528660c08401528060e0840152612e468184018688612bd8565b915050826101008301529b9a5050505050505050505050565b5f60208284031215612e6f575f80fd5b813563ffffffff811681146120bc575f80fd5b60c081525f612e9560c083018b8d612bd8565b8281036020840152612ea8818a8c612bd8565b90508281036040840152612ebd81888a612bd8565b63ffffffff969096166060840152505073ffffffffffffffffffffffffffffffffffffffff9290921660808301526bffffffffffffffffffffffff1660a0909101529695505050505050565b60e081525f612f1c60e083018c8e612bd8565b8281036020840152612f2f818b8d612bd8565b90508281036040840152612f4481898b612bd8565b905063ffffffff8716606084015273ffffffffffffffffffffffffffffffffffffffff861660808401526bffffffffffffffffffffffff851660a084015282810360c0840152612de18185612cbb565b5f81515f5b81811015612fb35760208185018101518683015201612f99565b505f93019283525090919050565b5f612fcc8285612f94565b60609390931b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001683525050601401919050565b5f6120bc8284612f9456fea2646970667358221220c4fc62ae071123245e22df482fd4745ad2d5a61e292e60ca558281668c58461964736f6c63430008170033000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a000000000000000000000000612e2daddc89d91409e40f946f9f7cfe422e777e00000000000000000000000069525dac489e4718964b5615c3d794a25d62beb7

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106100f0575f3560e01c80635599dfd711610093578063b590104e11610063578063b590104e14610234578063bfec346c1461025a578063cf2b5aa61461026d578063dbe3352a14610280575f80fd5b80635599dfd7146101e857806365c2cd91146101fb5780637e32d2411461020e5780637f9d22fe14610221575f80fd5b8063225cd1eb116100ce578063225cd1eb14610174578063228b131814610189578063331aaf9e146101af578063353ad881146101d5575f80fd5b80630a3ec799146100f45780631b39f9151461011a5780632059d9e21461012d575b5f80fd5b610107610102366004612399565b610293565b6040519081526020015b60405180910390f35b610107610128366004612428565b6102e9565b7f000000000000000000000000612e2daddc89d91409e40f946f9f7cfe422e777e5b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610111565b6101876101823660046124ae565b6104cc565b005b7f00000000000000000000000069525dac489e4718964b5615c3d794a25d62beb761014f565b7f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f61014f565b6101876101e3366004612526565b610639565b61014f6101f63660046125b6565b610d82565b61014f61020936600461261a565b610ed3565b61018761021c366004612693565b611026565b61018761022f366004612740565b61104c565b7f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a61014f565b61014f610268366004612790565b61105b565b61014f61027b3660046127e4565b6110d3565b61018761028e36600461285c565b611147565b5f6102a48a8a8a8a8a8a8a8a6102e9565b90505f5b888110156102db576102d38b8b8b848181106102c6576102c6612888565b9050602002013585611165565b6001016102a8565b509998505050505050505050565b5f868103610323576040517fa9d004a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8215801561033057508115155b15610367576040517f1a8a2c1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8415610427576103928989895f81811061038357610383612888565b905060200201358888886112ae565b905082156103be576103be8989895f8181106103b0576103b0612888565b9050602002013588856113b5565b60015b87811015610421576103ee8a8a8a848181106103df576103df612888565b905060200201358989896112ae565b508315610419576104198a8a8a8481811061040b5761040b612888565b9050602002013589866113b5565b6001016103c1565b506104c0565b831561045f576040517fbf2c1df000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82610496576040517f75ce98b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b878110156104be576104b68a8a8a8481811061040b5761040b612888565b600101610498565b505b98975050505050505050565b5f819003610506576040517f589a99ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b81811015610633575f83838381811061052357610523612888565b905060400201602001602081019061053b91906128e2565b600281111561054c5761054c6128b5565b03610587576105828484848481811061056757610567612888565b61057d9260206040909202019081019150612900565b611490565b61062b565b600183838381811061059b5761059b612888565b90506040020160200160208101906105b391906128e2565b60028111156105c4576105c46128b5565b036105fa57610582848484848181106105df576105df612888565b6105f59260206040909202019081019150612900565b61155d565b61062b8484848481811061061057610610612888565b6106269260206040909202019081019150612900565b61162a565b600101610508565b50505050565b8215801561064657508115155b1561067d576040517f1a8a2c1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82158015610689575084155b156106fd5785156106c5576040517e99fb3000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80156106fd576040517f69a1d31600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8415801561070a57508315155b15610741576040517fbf2c1df000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f2ab2b52b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152602482018990525f917f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f90911690632ab2b52b90604401602060405180830381865afa1580156107d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107fa919061291b565b90505f81156108b8576040517f9e79b41f000000000000000000000000000000000000000000000000000000008152600481018390527f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f73ffffffffffffffffffffffffffffffffffffffff1690639e79b41f9060240161010060405180830381865afa15801561088d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108b1919061293d565b60e0015190505b6040517f4635256e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b81166004830152602482018b90525f9182917f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f1690634635256e906044016040805180830381865afa15801561094c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109709190612a0a565b91509150825f148015610981575088155b80156109aa575073ffffffffffffffffffffffffffffffffffffffff82161580156109aa575086155b156109e1576040517f75ce98b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8883148015610a17575086158015610a0d575073ffffffffffffffffffffffffffffffffffffffff8216155b80610a1757508581145b15610bfd576040517f442559a200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d81166004830152602482018d90525f917f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f9091169063442559a290604401602060405180830381865afa158015610ab1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ad5919061291b565b6040517f7e04379500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8f81166004830152602482018f90529192505f917f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f1690637e04379590604401602060405180830381865afa158015610b6b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b8f919061291b565b90505f8c8314610ba857610ba48f8f8f6116f7565b5060015b878214610bbe57610bba8f8f8a6117cb565b5060015b80610bf5576040517f41deec4500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050610d74565b8815610c2f578215610c2057888314610c1b57610c1b848b8b61189f565b610c2f565b610c2d8c8c8c8c8c6112ae565b505b8615610c4857858114610c4857610c488c8c8c896113b5565b8680610c5357508815155b15610d27576040517f7e04379500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d81166004830152602482018d90525f917f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f90911690637e04379590604401602060405180830381865afa158015610ced573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d11919061291b565b9050858114610d2557610d258d8d886117cb565b505b88158015610d3457508215155b15610d4257610d428461195e565b86158015610d65575073ffffffffffffffffffffffffffffffffffffffff821615155b15610d7457610d748c8c611a17565b505050505050505050505050565b5f818015801590610d975750610d9781421190565b15610dce576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83608001355f03610e0b576040517fa87860d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401355f819003610e1b5750425b610e89610e288780612a36565b610e3560208a018a612a36565b610e4260408c018c612a36565b610e5060808d013589612a97565b7f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a8e6060016020810190610e849190612ad5565b611ae4565b92505f6040518060800160405280875f01358152602001876020013581526020018760400135815260200187606001358152509050610ec984825f611bce565b5050509392505050565b5f818015801590610ee85750610ee881421190565b15610f1f576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83608001355f03610f5c576040517fa87860d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608401355f819003610f6c5750425b610fdb610f798880612a36565b610f8660208b018b612a36565b610f9360408d018d612a36565b610fa160808d013589612a97565b7f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a8f6060016020810190610fd59190612ad5565b8f611cc9565b92505f6040518060800160405280875f0135815260200187602001358152602001876040013581526020018760600135815250905061101b84825f611bce565b505050949350505050565b6110438761103936899003890189612b00565b8787878787611db6565b50505050505050565b61104387878787878787611e94565b5f818015801590611070575061107081421190565b156110a7576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110b085611f85565b91506110cb826110c536879003870187612b00565b5f611bce565b509392505050565b5f8180158015906110e857506110e881421190565b1561111f576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61112986866120c3565b915061113e826110c536879003870187612b00565b50949350505050565b6111608361115a36859003850185612b00565b83611bce565b505050565b60405173ffffffffffffffffffffffffffffffffffffffff8416602482015260448101839052606481018290525f90819061123d907f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f90608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4eb1231700000000000000000000000000000000000000000000000000000000179052339190612207565b915091508115801561129957507fe5c3f2630000000000000000000000000000000000000000000000000000000061127482612b89565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b156112a7576112a7816122b7565b5050505050565b60405173ffffffffffffffffffffffffffffffffffffffff8616602482015260448101859052606481018490526084810183905260a481018290525f908190611394907f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f9060c401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8098531d000000000000000000000000000000000000000000000000000000001790523391906122fc565b9050808060200190518101906113aa919061291b565b979650505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff851660248201526044810184905260648101839052608481018290526112a7907f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f9060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f2fbbb25a000000000000000000000000000000000000000000000000000000001790523391906122fc565b6040516024810183905273ffffffffffffffffffffffffffffffffffffffff82166044820152611160907f00000000000000000000000069525dac489e4718964b5615c3d794a25d62beb790606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f05d8d4f2000000000000000000000000000000000000000000000000000000001790523391906122fc565b6040516024810183905273ffffffffffffffffffffffffffffffffffffffff82166044820152611160907f00000000000000000000000069525dac489e4718964b5615c3d794a25d62beb790606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095f0c70000000000000000000000000000000000000000000000000000000001790523391906122fc565b6040516024810183905273ffffffffffffffffffffffffffffffffffffffff82166044820152611160907f00000000000000000000000069525dac489e4718964b5615c3d794a25d62beb790606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f691aa51a000000000000000000000000000000000000000000000000000000001790523391906122fc565b60405173ffffffffffffffffffffffffffffffffffffffff841660248201526044810183905260648101829052610633907f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f90608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f574c8229000000000000000000000000000000000000000000000000000000001790523391906122fc565b60405173ffffffffffffffffffffffffffffffffffffffff841660248201526044810183905260648101829052610633907f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f90608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4eb12317000000000000000000000000000000000000000000000000000000001790523391906122fc565b604051602481018490526044810183905260648101829052610633907f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f90608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fe2cf968b000000000000000000000000000000000000000000000000000000001790523391906122fc565b611a137f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f8260405160240161199591815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f21506fff000000000000000000000000000000000000000000000000000000001790523391906122fc565b5050565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052611160907f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f90606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f21561935000000000000000000000000000000000000000000000000000000001790523391906122fc565b5f80611ba97f000000000000000000000000612e2daddc89d91409e40f946f9f7cfe422e777e8c8c8c8c8c8c8c8c8c604051602401611b2b99989796959493929190612c1f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f189267b2000000000000000000000000000000000000000000000000000000001790523391906122fc565b905080806020019051810190611bbf9190612ca0565b9b9a5050505050505050505050565b815160208301516040808501516060860151915173ffffffffffffffffffffffffffffffffffffffff8816602482015260448101949094526064840192909252608483019190915260a482015260c48101829052610633907f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a9060e401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f42017634000000000000000000000000000000000000000000000000000000001790523391906122fc565b5f80611d907f000000000000000000000000612e2daddc89d91409e40f946f9f7cfe422e777e8d8d8d8d8d8d8d8d8d8d604051602401611d129a99989796959493929190612d5c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa973d0e8000000000000000000000000000000000000000000000000000000001790523391906122fc565b905080806020019051810190611da69190612ca0565b9c9b505050505050505050505050565b611e8a7f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a88885f015189602001518a604001518b606001518b8b8b8b8b604051602401611e0c9a99989796959493929190612df2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9901261c000000000000000000000000000000000000000000000000000000001790523391906122fc565b5050505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff8816602482015260448101879052606481018690526084810185905260a4810184905260c4810183905260e48101829052611e8a907f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a9061010401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f7f9d22fe000000000000000000000000000000000000000000000000000000001790523391906122fc565b5f806120a67f000000000000000000000000612e2daddc89d91409e40f946f9f7cfe422e777e611fb58580612a36565b611fc26020880188612a36565b611fcf60408a018a612a36565b611fdf60808c0160608d01612e5f565b7f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a61201060a08e0160808f01612ad5565b60405160240161202899989796959493929190612e82565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fd061a9f2000000000000000000000000000000000000000000000000000000001790523391906122fc565b9050808060200190518101906120bc9190612ca0565b9392505050565b5f806121e97f000000000000000000000000612e2daddc89d91409e40f946f9f7cfe422e777e6120f38680612a36565b6121006020890189612a36565b61210d60408b018b612a36565b61211d60808d0160608e01612e5f565b7f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a8d60800160208101906121519190612ad5565b8d60405160240161216b9a99989796959493929190612f09565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f57848031000000000000000000000000000000000000000000000000000000001790523391906122fc565b9050808060200190518101906121ff9190612ca0565b949350505050565b5f60608373ffffffffffffffffffffffffffffffffffffffff168386604051602001612234929190612fc1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261226c91613000565b5f604051808303815f865af19150503d805f81146122a5576040519150601f19603f3d011682016040523d82523d5f602084013e6122aa565b606091505b5090969095509350505050565b8051156122c75780518082602001fd5b6040517f3cfe059f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b60605f61230a858585612207565b92509050806110cb576110cb826122b7565b73ffffffffffffffffffffffffffffffffffffffff811681146122f9575f80fd5b5f8083601f84011261234d575f80fd5b50813567ffffffffffffffff811115612364575f80fd5b6020830191508360208260051b850101111561237e575f80fd5b9250929050565b80358015158114612394575f80fd5b919050565b5f805f805f805f805f6101008a8c0312156123b2575f80fd5b89356123bd8161231c565b985060208a013567ffffffffffffffff8111156123d8575f80fd5b6123e48c828d0161233d565b90995097505060408a0135955060608a0135945060808a0135935061240b60a08b01612385565b925060c08a0135915060e08a013590509295985092959850929598565b5f805f805f805f8060e0898b03121561243f575f80fd5b883561244a8161231c565b9750602089013567ffffffffffffffff811115612465575f80fd5b6124718b828c0161233d565b90985096505060408901359450606089013593506080890135925061249860a08a01612385565b915060c089013590509295985092959890939650565b5f805f604084860312156124c0575f80fd5b83359250602084013567ffffffffffffffff808211156124de575f80fd5b818601915086601f8301126124f1575f80fd5b8135818111156124ff575f80fd5b8760208260061b8501011115612513575f80fd5b6020830194508093505050509250925092565b5f805f805f805f80610100898b03121561253e575f80fd5b88356125498161231c565b97506020890135965060408901359550606089013594506080890135935061257360a08a01612385565b925060c0890135915060e089013590509295985092959890939650565b5f608082840312156125a0575f80fd5b50919050565b5f60a082840312156125a0575f80fd5b5f805f60e084860312156125c8575f80fd5b833567ffffffffffffffff8111156125de575f80fd5b6125ea86828701612590565b9350506125fa85602086016125a6565b915060c084013590509250925092565b5f604082840312156125a0575f80fd5b5f805f80610100858703121561262e575f80fd5b843567ffffffffffffffff80821115612645575f80fd5b61265188838901612590565b95506020870135915080821115612666575f80fd5b506126738782880161260a565b93505061268386604087016125a6565b9396929550929360e00135925050565b5f805f805f805f610120888a0312156126aa575f80fd5b87356126b58161231c565b96506126c48960208a01612590565b955060a0880135945060c0880135935060e088013567ffffffffffffffff808211156126ee575f80fd5b818a0191508a601f830112612701575f80fd5b81358181111561270f575f80fd5b8b6020828501011115612720575f80fd5b602083019550809450505050610100880135905092959891949750929550565b5f805f805f805f60e0888a031215612756575f80fd5b87356127618161231c565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b5f805f60c084860312156127a2575f80fd5b833567ffffffffffffffff8111156127b8575f80fd5b6127c4868287016125a6565b9350506127d48560208601612590565b915060a084013590509250925092565b5f805f8060e085870312156127f7575f80fd5b843567ffffffffffffffff8082111561280e575f80fd5b61281a888389016125a6565b9550602087013591508082111561282f575f80fd5b5061283c8782880161260a565b93505061284c8660408701612590565b9396929550929360c00135925050565b5f805f60c0848603121561286e575f80fd5b83356128798161231c565b92506127d48560208601612590565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f602082840312156128f2575f80fd5b8135600381106120bc575f80fd5b5f60208284031215612910575f80fd5b81356120bc8161231c565b5f6020828403121561292b575f80fd5b5051919050565b80516123948161231c565b5f61010080838503121561294f575f80fd5b6040519081019067ffffffffffffffff82118183101715612997577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b81604052835191506129a88261231c565b818152602084015160208201526129c160408501612932565b6040820152606084015160608201526080840151608082015260a084015160a08201526129f060c08501612932565b60c082015260e084015160e0820152809250505092915050565b5f8060408385031215612a1b575f80fd5b8251612a268161231c565b6020939093015192949293505050565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612a69575f80fd5b83018035915067ffffffffffffffff821115612a83575f80fd5b60200191503681900382131561237e575f80fd5b80820180821115612acf577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b92915050565b5f60208284031215612ae5575f80fd5b81356bffffffffffffffffffffffff811681146120bc575f80fd5b5f60808284031215612b10575f80fd5b6040516080810181811067ffffffffffffffff82111715612b58577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b5f815160208301517fffffffff0000000000000000000000000000000000000000000000000000000080821693506004831015612bd05780818460040360031b1b83161693505b505050919050565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60c081525f612c3260c083018b8d612bd8565b8281036020840152612c45818a8c612bd8565b90508281036040840152612c5a81888a612bd8565b6060840196909652505073ffffffffffffffffffffffffffffffffffffffff9290921660808301526bffffffffffffffffffffffff1660a0909101529695505050505050565b5f60208284031215612cb0575f80fd5b81516120bc8161231c565b5f8135612cc78161231c565b73ffffffffffffffffffffffffffffffffffffffff1683526020820135368390037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1018112612d14575f80fd5b820160208101903567ffffffffffffffff811115612d30575f80fd5b803603821315612d3e575f80fd5b60406020860152612d53604086018284612bd8565b95945050505050565b60e081525f612d6f60e083018c8e612bd8565b8281036020840152612d82818b8d612bd8565b90508281036040840152612d9781898b612bd8565b905086606084015273ffffffffffffffffffffffffffffffffffffffff861660808401526bffffffffffffffffffffffff851660a084015282810360c0840152612de18185612cbb565b9d9c50505050505050505050505050565b5f61012073ffffffffffffffffffffffffffffffffffffffff8d1683528b60208401528a60408401528960608401528860808401528760a08401528660c08401528060e0840152612e468184018688612bd8565b915050826101008301529b9a5050505050505050505050565b5f60208284031215612e6f575f80fd5b813563ffffffff811681146120bc575f80fd5b60c081525f612e9560c083018b8d612bd8565b8281036020840152612ea8818a8c612bd8565b90508281036040840152612ebd81888a612bd8565b63ffffffff969096166060840152505073ffffffffffffffffffffffffffffffffffffffff9290921660808301526bffffffffffffffffffffffff1660a0909101529695505050505050565b60e081525f612f1c60e083018c8e612bd8565b8281036020840152612f2f818b8d612bd8565b90508281036040840152612f4481898b612bd8565b905063ffffffff8716606084015273ffffffffffffffffffffffffffffffffffffffff861660808401526bffffffffffffffffffffffff851660a084015282810360c0840152612de18185612cbb565b5f81515f5b81811015612fb35760208185018101518683015201612f99565b505f93019283525090919050565b5f612fcc8285612f94565b60609390931b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001683525050601401919050565b5f6120bc8284612f9456fea2646970667358221220c4fc62ae071123245e22df482fd4745ad2d5a61e292e60ca558281668c58461964736f6c63430008170033

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

000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a000000000000000000000000612e2daddc89d91409e40f946f9f7cfe422e777e00000000000000000000000069525dac489e4718964b5615c3d794a25d62beb7

-----Decoded View---------------
Arg [0] : _nftMarket (address): 0xcDA72070E455bb31C7690a170224Ce43623d0B6f
Arg [1] : _nftDropMarket (address): 0x53F451165Ba6fdbe39A134673d13948261B2334A
Arg [2] : _nftCollectionFactory (address): 0x612E2DadDc89d91409e40f946f9f7CfE422e777E
Arg [3] : _worlds (address): 0x69525Dac489e4718964B5615c3D794a25d62bEb7

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f
Arg [1] : 00000000000000000000000053f451165ba6fdbe39a134673d13948261b2334a
Arg [2] : 000000000000000000000000612e2daddc89d91409e40f946f9f7cfe422e777e
Arg [3] : 00000000000000000000000069525dac489e4718964b5615c3d794a25d62beb7


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.