Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
17380121 | 584 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
NFTDropMarket
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 25000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
/* ・ * ★ ・ 。 ・ ゚☆ 。 * ★ ゚・。 * 。 * ☆ 。・゚*.。 ゚ *.。☆。★ ・ ` .-:::::-.` `-::---...``` `-:` .:+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 "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./mixins/shared/FETHNode.sol"; import "./mixins/shared/FoundationTreasuryNode.sol"; import "./mixins/shared/Gap1000.sol"; import "./mixins/shared/Gap8500.sol"; import "./mixins/shared/MarketFees.sol"; import "./mixins/shared/MarketSharedCore.sol"; import "./mixins/shared/RouterContext.sol"; import "./mixins/shared/SendValueWithFallbackWithdraw.sol"; import "./mixins/shared/TxDeadline.sol"; import "./mixins/nftDropMarket/NFTDropMarketCore.sol"; import "./mixins/nftDropMarket/NFTDropMarketDutchAuction.sol"; import "./mixins/nftDropMarket/NFTDropMarketExhibition.sol"; import "./mixins/nftDropMarket/NFTDropMarketFixedPriceSale.sol"; error NFTDropMarket_NFT_Already_Minted(); /** * @title A market for minting NFTs with Foundation. * @author batu-inal & HardlyDifficult & philbirt & reggieag */ contract NFTDropMarket is TxDeadline, Initializable, FoundationTreasuryNode, Context, RouterContext, FETHNode, MarketSharedCore, Gap1000, ReentrancyGuardUpgradeable, SendValueWithFallbackWithdraw, MarketFees, Gap8500, NFTDropMarketCore, NFTDropMarketExhibition, NFTDropMarketFixedPriceSale, NFTDropMarketDutchAuction { /** * @notice Set immutable variables for the implementation contract. * @dev Using immutable instead of constants allows us to use different values on testnet. * @param treasury The Foundation Treasury contract address. * @param feth The FETH ERC-20 token contract address. * @param royaltyRegistry The Royalty Registry contract address. * @param nftMarket The NFT Market contract address, containing exhibition definitions. * @param router The trusted router contract address. */ constructor( address payable treasury, address feth, address royaltyRegistry, address nftMarket, address router ) FoundationTreasuryNode(treasury) FETHNode(feth) MarketFees( /* protocolFeeInBasisPoints: */ 1500, royaltyRegistry, /* assumePrimarySale: */ true ) NFTDropMarketExhibition(nftMarket) RouterContext(router) { _disableInitializers(); } /** * @notice Called once to configure the contract after the initial proxy deployment. * @dev This farms the initialize call out to inherited contracts as needed to initialize mutable variables. */ function initialize() external initializer { ReentrancyGuardUpgradeable.__ReentrancyGuard_init(); } /** * @inheritdoc MarketSharedCore * @dev Returns address(0) if the NFT has already been sold, otherwise checks for a listing in this market. */ function _getSellerOf( address nftContract, uint256 tokenId ) internal view override(MarketSharedCore, NFTDropMarketFixedPriceSale, NFTDropMarketDutchAuction) returns (address payable seller) { // Check the current owner first in case it has been sold. try IERC721(nftContract).ownerOf(tokenId) returns (address owner) { if (owner != address(0)) { // If sold, return address(0) since that owner cannot sell via this market. return payable(address(0)); } } catch { // Fall through } seller = super._getSellerOf(nftContract, tokenId); } /** * @inheritdoc NFTDropMarketCore */ function _getProtocolFee( address nftContract ) internal view override(MarketFees, NFTDropMarketCore) returns (uint256 protocolFeeInBasisPoints) { protocolFeeInBasisPoints = super._getProtocolFee(nftContract); } /** * @inheritdoc MarketSharedCore * @dev Reverts if the NFT has already been sold, otherwise checks for a listing in this market. */ function _getSellerOrOwnerOf( address nftContract, uint256 tokenId ) internal view override returns (address payable sellerOrOwner) { // Check the current owner first in case it has been sold. try IERC721(nftContract).ownerOf(tokenId) returns (address owner) { if (owner != address(0)) { // Once an NFT has been minted, it cannot be sold through this contract. revert NFTDropMarket_NFT_Already_Minted(); } } catch { // Fall through } sellerOrOwner = super._getSellerOf(nftContract, tokenId); } /** * @inheritdoc NFTDropMarketCore */ function _isCollectionListed( address nftContract ) internal override(NFTDropMarketCore, NFTDropMarketFixedPriceSale, NFTDropMarketDutchAuction) returns (bool isListed) { isListed = super._isCollectionListed(nftContract); } /** * @inheritdoc RouterContext */ function _msgSender() internal view override(Context, RouterContext) returns (address sender) { sender = super._msgSender(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @author: manifold.xyz import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @dev Royalty registry interface */ interface IRoyaltyRegistry is IERC165 { event RoyaltyOverride(address owner, address tokenAddress, address royaltyAddress); /** * Override the location of where to look up royalty information for a given token contract. * Allows for backwards compatibility and implementation of royalty logic for contracts that did not previously support them. * * @param tokenAddress - The token address you wish to override * @param royaltyAddress - The royalty override address */ function setRoyaltyLookupAddress(address tokenAddress, address royaltyAddress) external returns (bool); /** * Returns royalty address location. Returns the tokenAddress by default, or the override if it exists * * @param tokenAddress - The token address you are looking up the royalty for */ function getRoyaltyLookupAddress(address tokenAddress) external view returns (address); /** * Returns the token address that an overrideAddress is set for. * Note: will not be accurate if the override was created before this function was added. * * @param overrideAddress - The override address you are looking up the token for */ function getOverrideLookupTokenAddress(address overrideAddress) external view returns (address); /** * Whether or not the message sender can override the royalty address for the given token address * * @param tokenAddress - The token address you are looking up the royalty for */ function overrideAllowed(address tokenAddress) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * 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; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return 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 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); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} * * _Available since v4.7._ */ function verifyCalldata( bytes32[] calldata proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} * * _Available since v4.7._ */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). * * _Available since v4.7._ */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leavesLen = leaves.length; uint256 totalHashes = proofFlags.length; // Check proof validity. require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof"); // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { return hashes[totalHashes - 1]; } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leavesLen = leaves.length; uint256 totalHashes = proofFlags.length; // Check proof validity. require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof"); // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { return hashes[totalHashes - 1]; } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.2) (utils/introspection/ERC165Checker.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Library used to query support of an interface declared via {IERC165}. * * Note that these functions return the actual result of the query: they do not * `revert` if an interface is not supported. It is up to the caller to decide * what to do in these cases. */ library ERC165Checker { // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; /** * @dev Returns true if `account` supports the {IERC165} interface. */ function supportsERC165(address account) internal view returns (bool) { // Any contract that implements ERC165 must explicitly indicate support of // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid return supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) && !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID); } /** * @dev Returns true if `account` supports the interface defined by * `interfaceId`. Support for {IERC165} itself is queried automatically. * * See {IERC165-supportsInterface}. */ function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { // query support of both ERC165 as per the spec and support of _interfaceId return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId); } /** * @dev Returns a boolean array where each value corresponds to the * interfaces passed in and whether they're supported or not. This allows * you to batch check interfaces for a contract where your expectation * is that some interfaces may not be supported. * * See {IERC165-supportsInterface}. * * _Available since v3.4._ */ function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) { // an array of booleans corresponding to interfaceIds and whether they're supported or not bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); // query support of ERC165 itself if (supportsERC165(account)) { // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]); } } return interfaceIdsSupported; } /** * @dev Returns true if `account` supports all the interfaces defined in * `interfaceIds`. Support for {IERC165} itself is queried automatically. * * Batch-querying can lead to gas savings by skipping repeated checks for * {IERC165} support. * * See {IERC165-supportsInterface}. */ function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { // query support of ERC165 itself if (!supportsERC165(account)) { return false; } // query support of each interface in interfaceIds for (uint256 i = 0; i < interfaceIds.length; i++) { if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) { return false; } } // all interfaces supported return true; } /** * @notice Query if a contract implements an interface, does not check ERC165 support * @param account The address of the contract to query for support of an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract at account indicates support of the interface with * identifier interfaceId, false otherwise * @dev Assumes that account contains a contract that supports ERC165, otherwise * the behavior of this method is undefined. This precondition can be checked * with {supportsERC165}. * * Some precompiled contracts will falsely indicate support for a given interface, so caution * should be exercised when using this function. * * Interface identification is specified in ERC-165. */ function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) { // prepare call bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); // perform static call bool success; uint256 returnSize; uint256 returnValue; assembly { success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20) returnSize := returndatasize() returnValue := mload(0x00) } return success && returnSize >= 0x20 && returnValue > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// 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); }
// 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); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @title The required interface for collections to support minting from the NFTDropMarket. * @dev This interface must be registered as a ERC165 supported interface. * @author batu-inal & HardlyDifficult */ interface INFTLazyMintedCollectionMintCountTo { function mintCountTo(uint16 count, address to) external returns (uint256 firstTokenId); /** * @notice Get the number of tokens which can still be minted. * @return count The max number of additional NFTs that can be minted by this collection. */ function numberOfTokensAvailableToMint() external view returns (uint256 count); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @notice The required interface for collections in the NFTDropMarket to support exhibitions. * @author philbirt */ interface INFTMarketExhibition { function isAllowedSellerForExhibition( uint256 exhibitionId, address seller ) external view returns (bool allowedSeller); function getExhibitionPaymentDetails( uint256 exhibitionId ) external view returns (address payable curator, uint16 takeRateInBasisPoints); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @title Allows an approved minter to lock down supply changes for a limited period of time. * @dev This is used to help ensure minting access and token supply are not manipulated during an active minting period. * @author HardlyDifficult */ interface INFTSupplyLock { /** * @notice Request a supply lock for a limited period of time. * @param expiration The date/time when the lock expires, in seconds since the Unix epoch. * @dev The caller must already be an approved minter. * If a lock has already been requested, it may be cleared by the lock holder by passing 0 for the expiration. */ function minterAcquireSupplyLock(uint256 expiration) external; /** * @notice Get the current supply lock holder and expiration, if applicable. * @return supplyLockHolder The address of with lock access, or the zero address if supply is not locked. * @return supplyLockExpiration The date/time when the lock expires, in seconds since the Unix epoch. Returns 0 if a * lock has not been requested or if it has already expired. */ function getSupplyLock() external view returns (address supplyLockHolder, uint256 supplyLockExpiration); }
// 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); }
// 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); }
// 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 exhibitionId, uint256 price, uint256 limitPerAccount, uint256 generalAvailabilityStartTime, uint256 txDeadlineTime ) external; function createFixedPriceSaleWithEarlyAccessAllowlistV2( address nftContract, uint256 exhibitionId, uint256 price, uint256 limitPerAccount, uint256 generalAvailabilityStartTime, uint256 earlyAccessStartTime, bytes32 merkleRoot, string calldata merkleTreeUri, uint256 txDeadlineTime ) external; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @notice An interface for communicating fees to 3rd party marketplaces. * @dev Originally implemented in mainnet contract 0x44d6e8933f8271abcf253c72f9ed7e0e4c0323b3 */ interface IGetFees { /** * @notice Get the recipient addresses to which creator royalties should be sent. * @dev The expected royalty amounts are communicated with `getFeeBps`. * @param tokenId The ID of the NFT to get royalties for. * @return recipients An array of addresses to which royalties should be sent. */ function getFeeRecipients(uint256 tokenId) external view returns (address payable[] memory recipients); /** * @notice Get the creator royalty amounts to be sent to each recipient, in basis points. * @dev The expected recipients are communicated with `getFeeRecipients`. * @param tokenId The ID of the NFT to get royalties for. * @return royaltiesInBasisPoints The array of fees to be sent to each recipient, in basis points. */ function getFeeBps(uint256 tokenId) external view returns (uint256[] memory royaltiesInBasisPoints); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; interface IGetRoyalties { /** * @notice Get the creator royalties to be sent. * @dev The data is the same as when calling `getFeeRecipients` and `getFeeBps` separately. * @param tokenId The ID of the NFT to get royalties for. * @return recipients An array of addresses to which royalties should be sent. * @return royaltiesInBasisPoints The array of fees to be sent to each recipient, in basis points. */ function getRoyalties( uint256 tokenId ) external view returns (address payable[] memory recipients, uint256[] memory royaltiesInBasisPoints); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; interface IOwnable { /** * @dev Returns the address of the current owner. */ function owner() external view returns (address); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @notice Interface for EIP-2981: NFT Royalty Standard. * For more see: https://eips.ethereum.org/EIPS/eip-2981. */ interface IRoyaltyInfo { /** * @notice Get the creator royalties to be sent. * @param tokenId The ID of the NFT to get royalties for. * @param salePrice The total price of the sale. * @return receiver The address to which royalties should be sent. * @return royaltyAmount The total amount that should be sent to the `receiver`. */ function royaltyInfo( uint256 tokenId, uint256 salePrice ) external view returns (address receiver, uint256 royaltyAmount); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; interface ITokenCreator { /** * @notice Returns the creator of this NFT collection. * @param tokenId The ID of the NFT to get the creator payment address for. * @return creator The creator of this collection. */ function tokenCreator(uint256 tokenId) external view returns (address payable creator); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @title Helper functions for arrays. * @author batu-inal & HardlyDifficult */ library ArrayLibrary { /** * @notice Reduces the size of an array if it's greater than the specified max size, * using the first maxSize elements. */ function capLength(address payable[] memory data, uint256 maxLength) internal pure { if (data.length > maxLength) { assembly { mstore(data, maxLength) } } } /** * @notice Reduces the size of an array if it's greater than the specified max size, * using the first maxSize elements. */ function capLength(uint256[] memory data, uint256 maxLength) internal pure { if (data.length > maxLength) { assembly { mstore(data, maxLength) } } } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @title A library for calculating price for time based auctions. * @author HardlyDifficult & reggieag */ library AuctionPriceFunctions { /** * @notice Calculates the price from a linear curve at the given time. * @param maxPrice The maximum price per NFT, used at the start of the auction. * @param minPrice The minimum price per NFT which is slowly reached overtime, and becomes a fixed price after the * rest time has been reached. * @param startTime The start time of the auction. * @param endTime The time at which the auction reaches the final minPrice and no longer continues to decline, in * seconds since Unix epoch. * @param time The time at which to calculate the price. */ function getLinearlyDecreasingPriceAtTime( uint256 maxPrice, uint256 minPrice, uint256 startTime, uint256 endTime, uint256 time ) internal pure returns (uint256 price) { if (time <= startTime) { // Before an auction starts, return the initial price point. price = maxPrice; } else if (time >= endTime) { // After the auction ends, the price holds at the final minPrice. price = minPrice; } else { // During the auction, calculate the price from a linear curve. // price range price = maxPrice - minPrice; uint256 timeRemaining; unchecked { // Safe math not required, if endTime >= time then one of the ifs above would have been true. timeRemaining = endTime - time; } // price range * time remaining price *= timeRemaining; unchecked { // price range * time remaining / duration // Safe math not required, if startTime >= endTime then one of the ifs above would have been true. price /= endTime - startTime; } // price range * time remaining / duration + minPrice price += minPrice; } } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; /** * @title Helper library for interacting with Merkle trees & proofs. * @author batu-inal & HardlyDifficult & reggieag */ library MerkleAddressLibrary { using MerkleProof for bytes32[]; /** * @notice Gets the root for a merkle tree comprised only of addresses. */ function getMerkleRootForAddress(address user, bytes32[] calldata proof) internal pure returns (bytes32 root) { bytes32 leaf = keccak256(abi.encodePacked(user)); root = proof.processProofCalldata(leaf); } }
// 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; } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/access/IAccessControl.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "../../interfaces/internal/INFTLazyMintedCollectionMintCountTo.sol"; import "../shared/Constants.sol"; import "../shared/MarketFees.sol"; error NFTDropMarketCore_Collection_Already_Listed_For_Sale(); error NFTDropMarketCore_Invalid_Merkle_Root(); error NFTDropMarketCore_Invalid_Merkle_Tree_URI(); error NFTDropMarketCore_Mint_Permission_Required(); error NFTDropMarketCore_Must_Have_Available_Supply(); error NFTDropMarketCore_Must_Support_Collection_Mint_Interface(); error NFTDropMarketCore_Must_Support_ERC721(); error NFTDropMarketCore_Only_Callable_By_Collection_Owner(); error NFTDropMarketCore_Time_Too_Far_In_The_Future(uint256 maxTime); /** * @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 NFTDropMarketCore is Context, MarketFees { using ERC165Checker for address; /** * @notice The `role` type used to validate drop collections have granted this market access to mint. * @return `keccak256("MINTER_ROLE")` */ bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); modifier notListed(address nftContract) { if (_isCollectionListed(nftContract)) { revert NFTDropMarketCore_Collection_Already_Listed_For_Sale(); } _; } /// @notice Requires the given NFT contract can mint at least 1 more NFT. modifier notSoldOut(address nftContract) { if (INFTLazyMintedCollectionMintCountTo(nftContract).numberOfTokensAvailableToMint() == 0) { revert NFTDropMarketCore_Must_Have_Available_Supply(); } _; } /// @notice Requires the msg.sender has the admin role on the given NFT contract. modifier onlyCollectionAdmin(address nftContract) { if (!IAccessControl(nftContract).hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) { revert NFTDropMarketCore_Only_Callable_By_Collection_Owner(); } _; } /// @notice Requires the given NFT contract supports the interfaces currently required by this market for minting, /// and that it has granted this market the MINTER_ROLE. modifier onlySupportedCollectionType(address nftContract) { if (!nftContract.supportsInterface(type(INFTLazyMintedCollectionMintCountTo).interfaceId)) { // Must support the mint interface this market depends on. revert NFTDropMarketCore_Must_Support_Collection_Mint_Interface(); } if (!nftContract.supportsERC165InterfaceUnchecked(type(IERC721).interfaceId)) { // Must be an ERC-721 NFT collection. revert NFTDropMarketCore_Must_Support_ERC721(); } if (!IAccessControl(nftContract).hasRole(MINTER_ROLE, address(this))) { revert NFTDropMarketCore_Mint_Permission_Required(); } _; } /// @notice Requires the merkle params have been assigned non-zero values. modifier onlyValidMerkle(bytes32 merkleRoot, string calldata merkleTreeUri) { if (merkleRoot == bytes32(0)) { revert NFTDropMarketCore_Invalid_Merkle_Root(); } if (bytes(merkleTreeUri).length == 0) { revert NFTDropMarketCore_Invalid_Merkle_Tree_URI(); } _; } /// @notice Requires that the time provided is not more than 2 years in the future. modifier onlyValidScheduledTime(uint256 time) { // timestamp + 2 years can never realistically overflow 256 bits. unchecked { if (time > block.timestamp + MAX_SCHEDULED_TIME_IN_THE_FUTURE) { // Prevent arbitrarily large values from accidentally being set. revert NFTDropMarketCore_Time_Too_Far_In_The_Future(block.timestamp + MAX_SCHEDULED_TIME_IN_THE_FUTURE); } } _; } /** * @inheritdoc MarketFees * @dev Offers a reduced protocol fee for NFT edition collection sales. */ function _getProtocolFee( address nftContract ) internal view virtual override returns (uint256 protocolFeeInBasisPoints) { try INFTCollectionType(nftContract).getNFTCollectionType() returns (string memory nftCollectionType) { if (keccak256(abi.encodePacked(nftCollectionType)) == editionTypeHash) { return EDITION_PROTOCOL_FEE_IN_BASIS_POINTS; } } catch { // Fall through to use the default fee of 15% instead. // If the collection implements a fallback function, decoding will revert and cause the sale to fail. } return super._getProtocolFee(nftContract); } /** * @notice Returns true if the given NFT contract is already listed for sale in this market. * @param nftContract The NFT collection to check. */ function _isCollectionListed(address nftContract) internal virtual returns (bool isListed) { // Returns false by default. } /** * @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[1_000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "../../interfaces/internal/INFTSupplyLock.sol"; import "../../libraries/TimeLibrary.sol"; import "../../libraries/AuctionPriceFunctions.sol"; import "../shared/FETHNode.sol"; import "./NFTDropMarketCore.sol"; import "./NFTDropMarketExhibition.sol"; /// @param startTime The time when the auction starts and buyers may mint. error NFTDropMarketDutchAuction_Auction_Has_Not_Started_Yet(uint256 startTime); error NFTDropMarketDutchAuction_Auction_Not_Found(); /// @param endTime The time when the price reaches the minPrice. /// @param numberStillAvailable The number of NFTs still available to be minted. error NFTDropMarketDutchAuction_Clearing_Price_Not_Reached(uint256 endTime, uint256 numberStillAvailable); /// @param maxTime The maximum endTime (`startTime + saleDuration`) for an auction created now. error NFTDropMarketDutchAuction_End_Time_Too_Far_In_The_Future(uint256 maxTime); error NFTDropMarketDutchAuction_Creator_Revenue_Has_Already_Been_Withdrawn(); error NFTDropMarketDutchAuction_Limit_Per_Account_Must_Be_Set(); error NFTDropMarketDutchAuction_Min_Price_Must_Be_Less_Than_Max_Price(); error NFTDropMarketDutchAuction_Mint_Count_Must_Be_Greater_Than_Zero(); /// @param currentMintCount The number of NFTs that have already been minted. /// @param limitPerAccount The limit of NFTs that can be minted per account. error NFTDropMarketDutchAuction_Mint_Exceeds_Limit_Per_Account(uint256 currentMintCount, uint256 limitPerAccount); error NFTDropMarketDutchAuction_Must_Have_Available_Supply(); error NFTDropMarketDutchAuction_Nothing_To_Rebate_At_This_Time(); error NFTDropMarketDutchAuction_Sale_Duration_Must_Be_Greater_Than_Zero(); error NFTDropMarketDutchAuction_Start_Time_Must_Not_Be_In_The_Past(); /** * @title Allows creators to list a drop collection for sale via a dutch auction. * @notice The price per NFT declines overtime until the collection sells out or the minPrice is reached. * Collectors which buy in before this final clearing price point can request a rebate, such that all buyers end up * paying the same amount. * @author HardlyDifficult & reggieag. */ abstract contract NFTDropMarketDutchAuction is FETHNode, NFTDropMarketCore, NFTDropMarketExhibition { using SafeCast for uint256; using TimeLibrary for uint256; using TimeLibrary for uint32; //////////////////////////////////////////////////////////////// // Data //////////////////////////////////////////////////////////////// /// @notice Tracks purchases of a given buyer from a collection in dutch auction. struct DutchAuctionBuyerMintInfo { // Slot 0 /// @notice The total amount paid by the buyer for NFTs minted, minus any rebates sent. uint96 totalPosted; /// @notice The total number of NFTs minted by the buyer. uint16 mintedCount; // (144-bits free space) } /// @notice Dutch auction configuration and purchase tracking for a given collection. struct DutchAuctionInfo { // Slot 0 /// @notice The maximum price per NFT, used at the start of the auction. uint96 maxPrice; /// @notice The minimum price per NFT, reached if the auction doesn't sell out before the endTime. uint96 minPrice; /// @notice The time when the auction starts and buyers may mint. uint32 startTime; /// @notice The time when the price reaches the minPrice if it hasn't already minted out. uint32 endTime; // (slot full) // Slot 1 /// @notice The maximum number of NFTs that can be minted per account in this sale. uint16 limitPerAccount; /// @notice The total number of NFTs that have been minted in this sale so far. uint32 totalMintedCount; /// @notice The total number of NFTs that were available to be minted when the auction was created. uint32 totalAvailableSupply; /// @notice The last successful sale price. uint96 lastSalePrice; /// @notice True if the creator revenue has already been withdrawn. bool creatorRevenueHasBeenWithdrawn; // (72-bits free space) // Slot 2 /// @notice The account which listed this collection for sale. address payable seller; // (96-bits free space) // Slot 3 /// @notice Tracks purchases for a given buyer. mapping(address buyer => DutchAuctionBuyerMintInfo mintInfo) buyerToMintInfo; // (slot full) } /// @notice Stores dutch auction details for a given collection. mapping(address collection => DutchAuctionInfo auctionInfo) private $collectionToDutchAuctionInfo; //////////////////////////////////////////////////////////////// // Events //////////////////////////////////////////////////////////////// /** * @notice Emitted when a collection is listed for sale via a linear dutch auction. * @param nftContract The address of the NFT collection. * @param seller The account which listed this collection for sale. * @param maxPrice The maximum price per NFT, used at the start of the auction. * @param minPrice The minimum price per NFT, reached if the auction doesn't sell out before the endTime. * @param limitPerAccount The maximum number of NFTs that can be minted per account in this sale. * @param startTime The time when the auction starts and buyers may mint, in seconds since Unix epoch. * @param endTime The time when the price reaches the minPrice if it hasn't already minted out, in seconds since Unix * epoch. */ event CreateLinearDutchAuction( address indexed nftContract, address indexed seller, uint256 maxPrice, uint256 minPrice, uint256 limitPerAccount, uint256 startTime, uint256 endTime ); /** * @notice Emitted when a buyer mints one or more NFTs from a dutch auction sale. * @param nftContract The address of the NFT collection. * @param buyer The account which minted the NFT(s). * @param pricePaidPerNft The price per NFT paid by the buyer at the time of minting. * @param count The number of NFTs minted. * @param firstTokenId The tokenId for the first NFT minted. * The other minted tokens are assigned sequentially, so `firstTokenId` - `firstTokenId + count - 1` were minted. */ event MintFromDutchAuction( address indexed nftContract, address indexed buyer, uint256 pricePaidPerNft, uint256 count, uint256 firstTokenId ); /** * @notice Emitted when a buyer receives a rebate for the difference between the current price per NFT and the price * they have already paid. * @param nftContract The address of the NFT collection. * @param buyer The account which received the rebate for earlier mint(s). * @param rebate The amount of ETH returned to the buyer. * @param currentPricePerNft The current price per NFT at the time of this rebate. * @dev If the price per NFT drops below currentPricePerNft then another rebate may be requested for this buyer. */ event RebateBuyerFromDutchAuction( address indexed nftContract, address indexed buyer, uint256 rebate, uint256 currentPricePerNft ); /** * @notice Emitted when the creator revenue is withdrawn after a dutch auction has reached the final clearing price. * @param nftContract The address of the NFT collection. * @param clearingPrice The final clearing price per NFT. * @param totalMintedCount The total number of NFTs minted. * @param totalFees The total fees collected. * @param creatorRev The amount of ETH withdrawn by the creator. */ event WithdrawCreatorRevenueFromDutchAuction( address indexed nftContract, uint256 clearingPrice, uint256 totalMintedCount, uint256 totalFees, uint256 creatorRev ); //////////////////////////////////////////////////////////////// // Creating auctions //////////////////////////////////////////////////////////////// /** * @notice Allows an admin of a collection to create a new dutch auction, where the price declines linearly over time * during the sale until it either sells out or the minPrice is reached. * @param nftContract The address of the NFT drop collection. * @param exhibitionId The exhibition to associate this sale to. Set to 0 to exist outside of an exhibition. * @param maxPrice The maximum price per NFT, used at the start of the auction. This value must be non-zero and * greater than the minPrice, but less than ~70 billion ETH. Set in wei. * @param minPrice The minimum price per NFT which is slowly reached overtime, and becomes a fixed price after the * endTime has been reached. Set in wei and this value may be 0. * @param limitPerAccount The max number of NFTs an account may mint in this sale. A value between 1 and 65,535 is * required. * @param startTime The time at which the auction should start, in seconds since Unix epoch. Pass 0 to have the * auction start immediately. * @param saleDuration The length of time after startTime until the auction reaches the minPrice and no longer * continues to decline, in seconds since Unix epoch. The auction's endTime (startTime + saleDuration) must be in * <= 2 years. * @dev Notes: * a) The sale is final and can not be updated or canceled. * b) Any collection that abides by `INFTLazyMintedCollectionMintCountTo`, `IAccessControl`, and `INFTSupplyLock` * is supported. */ function createLinearDutchAuction( address nftContract, uint256 exhibitionId, uint256 maxPrice, uint256 minPrice, uint256 limitPerAccount, uint256 startTime, uint256 saleDuration ) external onlySupportedCollectionType(nftContract) onlyCollectionAdmin(nftContract) notListed(nftContract) { /* CHECKS */ // Price must decline over time. if (maxPrice <= minPrice) { revert NFTDropMarketDutchAuction_Min_Price_Must_Be_Less_Than_Max_Price(); } // A non-zero limit is required. if (limitPerAccount == 0) { revert NFTDropMarketDutchAuction_Limit_Per_Account_Must_Be_Set(); } // The sale must start now or in the future. if (startTime == 0) { // When startTime is 0, the sale starts immediately. startTime = block.timestamp; } else if (startTime.hasExpired()) { revert NFTDropMarketDutchAuction_Start_Time_Must_Not_Be_In_The_Past(); } // The sale must run for > 0 seconds and end in <= 2 years. if (saleDuration == 0) { revert NFTDropMarketDutchAuction_Sale_Duration_Must_Be_Greater_Than_Zero(); } uint256 endTime = startTime + saleDuration; unchecked { // timestamp + 2 years can never realistically overflow 256 bits. if (endTime > block.timestamp + MAX_SCHEDULED_TIME_IN_THE_FUTURE) { // Prevent arbitrarily large values from accidentally being set. revert NFTDropMarketDutchAuction_End_Time_Too_Far_In_The_Future( block.timestamp + MAX_SCHEDULED_TIME_IN_THE_FUTURE ); } } // The collection must have NFTs available to mint. uint256 availableToMint = INFTLazyMintedCollectionMintCountTo(nftContract).numberOfTokensAvailableToMint(); if (availableToMint == 0) { revert NFTDropMarketDutchAuction_Must_Have_Available_Supply(); } address payable sender = payable(_msgSender()); DutchAuctionInfo storage auctionInfo = $collectionToDutchAuctionInfo[nftContract]; /* EFFECTS */ // Splitting storage write (by slot) to avoid stack too deep (auctionInfo.maxPrice, auctionInfo.minPrice, auctionInfo.startTime, auctionInfo.endTime) = ( maxPrice.toUint96(), // Safe cast is not required since minPrice is less than maxPrice which is also uint96. uint96(minPrice), // Safe cast is not required on the times since MAX_SCHEDULED_TIME_IN_THE_FUTURE confirms the max is in range // until 2106. Checking the endTime implicitly ensures that the startTime is also not too far in the future. uint32(startTime), uint32(endTime) ); (auctionInfo.limitPerAccount, auctionInfo.totalAvailableSupply, auctionInfo.seller) = ( limitPerAccount.toUint16(), // If a huge number of NFTs are available, this will revert & prevent it from being listed in a dutch auction. availableToMint.toUint32(), sender ); // totalMintedCount, lastSalePrice and creatorRevenueHasBeenWithdrawn default to 0/false /* INTERACTIONS */ // If the seller/collection is not authorized to list with this exhibition in the NFTMarket, this will revert. // This interaction is with a trusted contract. _addCollectionToExhibition(nftContract, exhibitionId); // If a supply lock is not supported, this will revert blocking the ability to use dutch auctions. // This will also revert if the collection has already promised the lock to another market or user. INFTSupplyLock(nftContract).minterAcquireSupplyLock(endTime); emit CreateLinearDutchAuction(nftContract, sender, maxPrice, minPrice, limitPerAccount, startTime, endTime); } /** * @inheritdoc NFTDropMarketCore */ function _isCollectionListed(address nftContract) internal virtual override returns (bool isListed) { isListed = $collectionToDutchAuctionInfo[nftContract].seller != address(0) || super._isCollectionListed(nftContract); } //////////////////////////////////////////////////////////////// // Minting //////////////////////////////////////////////////////////////// /** * @notice Mints NFTs from a dutch auction sale. * @param nftContract The address of the NFT collection. * @param count The number of NFTs to mint. * @return firstTokenId The ID of the first token minted. * The other minted tokens are assigned sequentially, so `firstTokenId` - `firstTokenId + count - 1` were minted. */ function mintFromDutchAuction(address nftContract, uint256 count) external payable returns (uint256 firstTokenId) { /* CHECKS */ // A non-zero count is required. if (count == 0) { revert NFTDropMarketDutchAuction_Mint_Count_Must_Be_Greater_Than_Zero(); } DutchAuctionInfo storage auctionInfo = $collectionToDutchAuctionInfo[nftContract]; // The collection must be listed for sale in dutch auction. if (auctionInfo.endTime == 0) { // This check uses endTime as a proxy for auction exists since that slot is always read anyways during minting. revert NFTDropMarketDutchAuction_Auction_Not_Found(); } // The auction must have started. if (!auctionInfo.startTime.hasBeenReached()) { revert NFTDropMarketDutchAuction_Auction_Has_Not_Started_Yet(auctionInfo.startTime); } address payable sender = payable(_msgSender()); DutchAuctionBuyerMintInfo storage buyerMintInfo = auctionInfo.buyerToMintInfo[sender]; // Check if the buyer has exhausted their mint limit. uint256 buyerMintCount = count + buyerMintInfo.mintedCount; if (buyerMintCount > auctionInfo.limitPerAccount) { revert NFTDropMarketDutchAuction_Mint_Exceeds_Limit_Per_Account( buyerMintInfo.mintedCount, auctionInfo.limitPerAccount ); } // Calculate the price and cost. uint256 pricePerNft = getPriceAtTimeForDutchAuction(nftContract, block.timestamp); uint256 totalCost; unchecked { // Safe math is not required because price is uint96 and count is capped by the limitPerAccount which is uint16. totalCost = pricePerNft * count; } bool creatorRevenueHasBeenWithdrawn = auctionInfo.creatorRevenueHasBeenWithdrawn; /* EFFECTS */ (auctionInfo.totalMintedCount, auctionInfo.lastSalePrice) = ( // Safe cast not required for count / buyerMintCount, since they're capped by the limitPerAccount which is uint16. uint32(count + auctionInfo.totalMintedCount), // Safe cast is not required since the max and min prices are uint96. uint96(pricePerNft) ); (buyerMintInfo.totalPosted, buyerMintInfo.mintedCount) = ( (totalCost + buyerMintInfo.totalPosted).toUint96(), uint16(buyerMintCount) ); /* INTERACTIONS */ // If the msg.value was not enough to cover the cost, attempt to use the buyer's FETH balance. If insufficient FETH // is available this will revert the mint. If too much msg.value was provided, this will refund the surplus. // This interaction is with a trusted contract. _tryUseFETHBalance({ totalAmount: totalCost, shouldRefundSurplus: true }); // Mint NFT(s) for the buyer. firstTokenId = INFTLazyMintedCollectionMintCountTo(nftContract).mintCountTo(uint16(count), sender); if (creatorRevenueHasBeenWithdrawn) { // Once the revenue has been withdrawn, sales distribute funds immediately instead of storing the value in escrow. // seller does not need to be cached before interactions since the field is immutable once an auction has been // created. _distributeFundsFromSale(nftContract, totalCost, auctionInfo.seller, firstTokenId); } emit MintFromDutchAuction(nftContract, sender, pricePerNft, count, firstTokenId); } //////////////////////////////////////////////////////////////// // Buyer post sale //////////////////////////////////////////////////////////////// /** * @notice Sends a buyer the difference between the amount they paid and the current price point for this dutch * auction. * @param nftContract The NFT collection address. * @param buyer The buyer to rebate. * @dev This may be called by anyone on behalf of the buyer, and may be called multiple times if the price continues * to decline after a previous call. */ function rebateBuyerFromDutchAuction(address nftContract, address payable buyer) external { /* CHECKS */ DutchAuctionBuyerMintInfo storage buyerMintInfo = $collectionToDutchAuctionInfo[nftContract].buyerToMintInfo[buyer]; (uint256 mintedCount, uint256 totalPosted) = (buyerMintInfo.mintedCount, buyerMintInfo.totalPosted); uint256 currentPricePerNft = getPriceAtTimeForDutchAuction(nftContract, block.timestamp); uint256 requiredAmount; unchecked { // Safe math is not required because the maxPrice is uint96 and mintedCount is uint16. requiredAmount = currentPricePerNft * mintedCount; } uint256 rebate = totalPosted - requiredAmount; if (rebate == 0) { revert NFTDropMarketDutchAuction_Nothing_To_Rebate_At_This_Time(); } /* EFFECTS */ // Safe cast is not required since requiredAmount is < totalPosted which is also uint96. buyerMintInfo.totalPosted = uint96(requiredAmount); /* INTERACTIONS */ _sendValueWithFallbackWithdraw(buyer, rebate, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS); emit RebateBuyerFromDutchAuction(nftContract, buyer, rebate, currentPricePerNft); } /** * @notice Checks a buyer's pending rebate from previous purchase and the max number of NFTs they may still mint from * the dutch auction. * @param nftContract The address of the NFT collection. * @param buyer The address which has or will be minting. * @return outstandingRebateBalance The amount available to be rebated to this buyer. * @return numberThatCanBeMinted How many NFTs the buyer can still mint. */ function getBuyerInfoFromDutchAuction( address nftContract, address buyer ) external view returns (uint256 outstandingRebateBalance, uint256 numberThatCanBeMinted) { DutchAuctionInfo storage auctionInfo = $collectionToDutchAuctionInfo[nftContract]; DutchAuctionBuyerMintInfo memory buyerMintInfo = auctionInfo.buyerToMintInfo[buyer]; uint256 currentTotalCost; unchecked { // Safe math is not required because the maxPrice is uint96 and mintedCount is uint16. currentTotalCost = getPriceAtTimeForDutchAuction(nftContract, block.timestamp) * buyerMintInfo.mintedCount; } outstandingRebateBalance = buyerMintInfo.totalPosted - currentTotalCost; uint256 limitPerAccount = auctionInfo.limitPerAccount; // If the buyer has exhausted their limit then the number that can be minted is the default of 0. if (buyerMintInfo.mintedCount < limitPerAccount) { unchecked { // Safe math is not required due to the if statement directly above. numberThatCanBeMinted = limitPerAccount - buyerMintInfo.mintedCount; } uint256 availableSupply = auctionInfo.totalAvailableSupply; uint256 totalMintedCount = auctionInfo.totalMintedCount; if (availableSupply > totalMintedCount) { unchecked { // Safe math is not required due to the if statement directly above. availableSupply -= totalMintedCount; } if (numberThatCanBeMinted > availableSupply) { // The buyer's mint limit is limited by the available supply remaining. numberThatCanBeMinted = availableSupply; } } else { // The collection has sold out. numberThatCanBeMinted = 0; } } } //////////////////////////////////////////////////////////////// // Seller post sale //////////////////////////////////////////////////////////////// /** * @notice Sends the creator revenue from a dutch auction which has sold out or reached the endTime / minPrice. * @param nftContract The address of the NFT collection. * @dev Anyone may call this on behalf of the creator. This will also pay the treasury and split recipients if * applicable. Once called, any future sales at the final clearing price will send funds as purchases are made. This * is callable even if no funds to withdraw but it may only be called once per sale. */ function withdrawCreatorRevenueFromDutchAuction(address nftContract) external { /* CHECKS */ DutchAuctionInfo storage auctionInfo = $collectionToDutchAuctionInfo[nftContract]; address payable seller = auctionInfo.seller; // Auction must have been created. if (seller == address(0)) { revert NFTDropMarketDutchAuction_Auction_Not_Found(); } // Withdraw may only be called once. if (auctionInfo.creatorRevenueHasBeenWithdrawn) { revert NFTDropMarketDutchAuction_Creator_Revenue_Has_Already_Been_Withdrawn(); } // Auction must be sold out or has reached the endTime / minPrice. uint256 totalMintedCount = auctionInfo.totalMintedCount; if (!auctionInfo.endTime.hasExpired() && totalMintedCount < auctionInfo.totalAvailableSupply) { unchecked { revert NFTDropMarketDutchAuction_Clearing_Price_Not_Reached( auctionInfo.endTime, // Safe math is not required thanks to the above if check. auctionInfo.totalAvailableSupply - totalMintedCount ); } } // Calculate the total sale amount for distribution. uint256 clearingPrice = getPriceAtTimeForDutchAuction(nftContract, block.timestamp); uint256 totalSale; unchecked { // Safe math is not required since the max and min prices are uint96 and totalMintedCount is uint32. totalSale = clearingPrice * totalMintedCount; } /* EFFECTS */ auctionInfo.creatorRevenueHasBeenWithdrawn = true; /* INTERACTIONS */ (uint256 totalFees, uint256 creatorRev) = _distributeFundsFromSale({ nftContract: nftContract, totalSale: totalSale, seller: seller, tokenId: 0 // Unknown since this may represent many sales. }); emit WithdrawCreatorRevenueFromDutchAuction(nftContract, clearingPrice, totalMintedCount, totalFees, creatorRev); } /** * @notice Checks seller related information for a collection which has been scheduled in a dutch auction. * @param nftContract The address of the NFT collection. * @return seller The address of the user which listed the collection for sale. * @return creatorRevenueReadyForWithdrawal True if the auction has reached a final clearing price and revenue is * ready to be withdrawn. This will be false if the creator revenue has already been withdrawn. * @return creatorRevenueHasBeenWithdrawn True if the creator revenue has already been withdrawn. * @return totalFundsPendingDistribution The total amount of funds which are pending distribution. */ function getSellerInfoFromDutchAuction( address nftContract ) external view returns ( address seller, bool creatorRevenueReadyForWithdrawal, bool creatorRevenueHasBeenWithdrawn, uint256 totalFundsPendingDistribution ) { DutchAuctionInfo storage auctionInfo = $collectionToDutchAuctionInfo[nftContract]; // Return default values if an auction has not been created yet. if (auctionInfo.endTime != 0) { seller = auctionInfo.seller; creatorRevenueHasBeenWithdrawn = auctionInfo.creatorRevenueHasBeenWithdrawn; // creatorRevenueReadyForWithdrawal and totalFundsPendingDistribution are n/a if the creator revenue has already // been withdrawn. if (!creatorRevenueHasBeenWithdrawn) { creatorRevenueReadyForWithdrawal = // The auction has sold out. auctionInfo.totalMintedCount >= auctionInfo.totalAvailableSupply || // Or it's past the auction's endTime. auctionInfo.endTime.hasExpired(); unchecked { // Safe math is not required because the maxPrice is uint96 and totalMintedCount is uint32. totalFundsPendingDistribution = getPriceAtTimeForDutchAuction(nftContract, block.timestamp) * auctionInfo.totalMintedCount; } } } } /** * @notice A helper to distribute funds from a dutch auction sale. * @param nftContract The NFT collection address. * @param totalSale The total ETH sale amount. * @param seller The seller which listed the collection for sale. */ function _distributeFundsFromSale( address nftContract, uint256 totalSale, address payable seller, uint256 tokenId ) private returns (uint256 totalFees, uint256 creatorRev) { // These values do not need to be cached before interactions since the fields cannot be modified other than to // delete the exhibition, resulting in 0 values being returned here which does not pose a security risk. (address payable curator, uint16 takeRateInBasisPoints) = _getExhibitionByCollection(nftContract); // sellerRev is always 0 and ignored here since mints are primary sales, revenue is attributed to the creator. (totalFees, creatorRev, ) = _distributeFunds({ nftContract: nftContract, tokenId: tokenId, seller: seller, price: totalSale, buyReferrer: payable(0), // Not supported sellerReferrerPaymentAddress: curator, sellerReferrerTakeRateInBasisPoints: takeRateInBasisPoints }); } /** * @inheritdoc MarketSharedCore * @dev Returns the seller for a collection if listed in a dutch auction. */ function _getSellerOf( address nftContract, uint256 tokenId ) internal view virtual override returns (address payable seller) { seller = $collectionToDutchAuctionInfo[nftContract].seller; if (seller == address(0)) { seller = super._getSellerOf(nftContract, tokenId); } } //////////////////////////////////////////////////////////////// // General Getters //////////////////////////////////////////////////////////////// /** * @notice Returns information about a given dutch auction. * @param nftContract The address of the NFT collection which was listed for sale in a dutch auction. * @return maxPrice The maximum price per NFT, used at the start of the auction. * @return minPrice The minimum price per NFT which is slowly reached overtime, and becomes a fixed price after the * endTime has been reached. This value may be 0. * @return limitPerAccount The max number of NFTs an account may mint in this sale. * @return startTime The time at which the auction should start, in seconds since Unix epoch. * @return endTime The time at which the auction reaches the minPrice and no longer continues to decline, in seconds * since Unix epoch. * @return totalAvailableSupply The collection's available supply when the auction was originally created. * @return totalMintedCount The number of NFTs minted via this dutch auction. * @return lastSalePrice The price per NFT of the last recorded sale, or 0 if no sales have been made. * @return currentPrice The current price per NFT. * @dev To determine if price action has ended / the clearing price has been reached - check if the endTime has * expired or if totalMintedCount == totalAvailableSupply. */ function getDutchAuction( address nftContract ) external view returns ( uint256 maxPrice, uint256 minPrice, uint256 limitPerAccount, uint256 startTime, uint256 endTime, uint256 totalAvailableSupply, uint256 totalMintedCount, uint256 lastSalePrice, uint256 currentPrice ) { DutchAuctionInfo storage auctionInfo = $collectionToDutchAuctionInfo[nftContract]; maxPrice = auctionInfo.maxPrice; minPrice = auctionInfo.minPrice; limitPerAccount = auctionInfo.limitPerAccount; startTime = auctionInfo.startTime; endTime = auctionInfo.endTime; totalAvailableSupply = auctionInfo.totalAvailableSupply; totalMintedCount = auctionInfo.totalMintedCount; lastSalePrice = auctionInfo.lastSalePrice; currentPrice = getPriceAtTimeForDutchAuction(nftContract, block.timestamp); } /** * @notice Get the price per NFT for sale in a dutch auction if purchased at the given time. * @param nftContract The NFT collection address. * @param time Time in seconds since the Unix epoch to calculate the price for. * @return price The price per NFT at the time provided. * @dev This should not be used for historical prices, once the collection sells out this always returns the final * clearing price and not the correct historical value. */ function getPriceAtTimeForDutchAuction(address nftContract, uint256 time) public view returns (uint256 price) { DutchAuctionInfo storage auctionInfo = $collectionToDutchAuctionInfo[nftContract]; if (auctionInfo.totalMintedCount >= auctionInfo.totalAvailableSupply) { // All tokens have been minted, so the final clearing price is the last successful sale price. price = auctionInfo.lastSalePrice; } else { // Not all tokens have been minted, so calculate current price based on auction config and the time provided. price = AuctionPriceFunctions.getLinearlyDecreasingPriceAtTime( auctionInfo.maxPrice, auctionInfo.minPrice, auctionInfo.startTime, auctionInfo.endTime, time ); } } /** * @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 1,000 slots in total. */ uint256[999] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "../../interfaces/internal/INFTMarketExhibition.sol"; error NFTDropMarketExhibition_Exhibition_Does_Not_Exist(); error NFTDropMarketExhibition_NFT_Market_Is_Not_A_Contract(); error NFTDropMarketExhibition_Seller_Not_Allowed_In_Exhibition(); /** * @title Enables a curation surface for sellers to exhibit their NFTs in the drop market. * @author HardlyDifficult & philbirt */ abstract contract NFTDropMarketExhibition is Context { using AddressUpgradeable for address; /// @notice Maps a collection to the exhibition it was listed with. mapping(address => uint256) private nftContractToExhibitionId; /// @notice The NFT Market contract address, containing exhibition definitions. address private immutable _nftMarket; /** * @notice Emitted when a collection is added to an exhibition. * @param nftContract The contract address of the collection. * @param exhibitionId The ID of the exhibition the collection was added to. */ event CollectionAddedToExhibition(address indexed nftContract, uint256 indexed exhibitionId); /** * @notice Set immutable variables for the implementation contract. * @dev Using immutable instead of constants allows us to use different values on testnet. * @param nftMarket The NFT Market contract address, containing exhibition definitions. */ constructor(address nftMarket) { if (!nftMarket.isContract()) { revert NFTDropMarketExhibition_NFT_Market_Is_Not_A_Contract(); } _nftMarket = nftMarket; } /** * @notice Calls the trusted NFTMarket to authorize adding this collection to the exhibition, and then stores the * relationship. * @dev No-op if called with exhibitionId 0. */ function _addCollectionToExhibition(address nftContract, uint256 exhibitionId) internal { if (exhibitionId != 0) { // If there is an exhibition, make sure the seller is allowed to list in it if ( !INFTMarketExhibition(_nftMarket).isAllowedSellerForExhibition({ exhibitionId: exhibitionId, seller: _msgSender() }) ) { (address curator, ) = INFTMarketExhibition(_nftMarket).getExhibitionPaymentDetails(exhibitionId); if (curator == address(0)) { // Provides a more useful error when an exhibition never existed or has since been deleted. revert NFTDropMarketExhibition_Exhibition_Does_Not_Exist(); } revert NFTDropMarketExhibition_Seller_Not_Allowed_In_Exhibition(); } nftContractToExhibitionId[nftContract] = exhibitionId; emit CollectionAddedToExhibition(nftContract, exhibitionId); } } /** * @notice Returns the exhibition ID for a given Collection. * @param nftContract The contract address of the Collection. * @return exhibitionId The ID of the exhibition this Collection is assigned to, or 0 if it's * not assigned to an exhibition. */ function getExhibitionIdForCollection(address nftContract) external view returns (uint256 exhibitionId) { exhibitionId = nftContractToExhibitionId[nftContract]; } /** * @notice Returns the contract which contains the exhibition definitions. * @return nftMarket The NFT Market contract address. */ function getNftMarket() external view returns (address nftMarket) { nftMarket = _nftMarket; } /** * @notice Returns the exhibition payment details for a given Collection. * @dev The payment address and take rate will be 0 if the collection is not assigned to an exhibition or if the * exhibition has seen been deleted. */ function _getExhibitionByCollection( address nftContract ) internal view returns (address payable curator, uint16 takeRateInBasisPoints) { uint256 exhibitionId = nftContractToExhibitionId[nftContract]; if (exhibitionId != 0) { (curator, takeRateInBasisPoints) = INFTMarketExhibition(_nftMarket).getExhibitionPaymentDetails(exhibitionId); } } /** * @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 1,000 slots in total. */ uint256[999] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "../../interfaces/internal/INFTLazyMintedCollectionMintCountTo.sol"; import "../../interfaces/internal/routes/INFTDropMarketFixedPriceSale.sol"; import "../../libraries/MerkleAddressLibrary.sol"; import "../../libraries/TimeLibrary.sol"; import "../shared/MarketFees.sol"; import "../shared/TxDeadline.sol"; import "./NFTDropMarketCore.sol"; import "./NFTDropMarketExhibition.sol"; /// @param limitPerAccount The limit of tokens an account can purchase. error NFTDropMarketFixedPriceSale_Cannot_Buy_More_Than_Limit(uint256 limitPerAccount); /// @param earlyAccessStartTime The time when early access starts, in seconds since the Unix epoch. error NFTDropMarketFixedPriceSale_Early_Access_Not_Open(uint256 earlyAccessStartTime); error NFTDropMarketFixedPriceSale_Early_Access_Start_Time_Has_Expired(); error NFTDropMarketFixedPriceSale_General_Access_Is_Open(); /// @param generalAvailabilityStartTime The start time of the general availability period, in seconds since the Unix /// epoch. error NFTDropMarketFixedPriceSale_General_Access_Not_Open(uint256 generalAvailabilityStartTime); error NFTDropMarketFixedPriceSale_General_Availability_Start_Time_Has_Expired(); error NFTDropMarketFixedPriceSale_Invalid_Merkle_Proof(); error NFTDropMarketFixedPriceSale_Limit_Per_Account_Must_Be_Set(); error NFTDropMarketFixedPriceSale_Must_Be_Listed_For_Sale(); error NFTDropMarketFixedPriceSale_Must_Buy_At_Least_One_Token(); error NFTDropMarketFixedPriceSale_Must_Have_Non_Zero_Early_Access_Duration(); /// @param mintCost The total cost for this purchase. error NFTDropMarketFixedPriceSale_Too_Much_Value_Provided(uint256 mintCost); /** * @title Allows creators to list a drop collection for sale at a fixed price point. * @dev Listing a collection for sale in this market requires the collection to implement * the functions in `INFTLazyMintedCollectionMintCountTo` and to register that interface with ERC165. * Additionally the collection must implement access control, or more specifically: * `hasRole(bytes32(0), msg.sender)` must return true when called from the creator or admin's account * and `hasRole(keccak256("MINTER_ROLE", address(this)))` must return true for this market's address. * @author batu-inal & HardlyDifficult & philbirt & reggieag */ abstract contract NFTDropMarketFixedPriceSale is INFTDropMarketFixedPriceSale, TxDeadline, Context, NFTDropMarketCore, NFTDropMarketExhibition { using MerkleAddressLibrary for address; using SafeCast for uint256; using TimeLibrary for uint32; using TimeLibrary for uint256; /** * @notice Configuration for the terms of the sale. */ struct FixedPriceSaleConfig { /****** Slot 0 (of this struct) ******/ /// @notice The seller for the drop. address payable seller; /// @notice The fixed price per NFT in the collection. /// @dev The maximum price that can be set on an NFT is ~1.2M (2^80/10^18) ETH. uint80 price; /// @notice The max number of NFTs an account may mint in this sale. uint16 limitPerAccount; /****** Slot 1 ******/ /// @notice Tracks how many NFTs a given user has already minted. mapping(address => uint256) userToMintedCount; /****** Slot 2 ******/ /// @notice The start time of the general availability period, in seconds since the Unix epoch. /// @dev This must be >= `earlyAccessStartTime`. /// When set to 0, general availability was not scheduled and started as soon as the price was set. uint32 generalAvailabilityStartTime; /// @notice The time when early access purchasing may begin, in seconds since the Unix epoch. /// @dev This must be <= `generalAvailabilityStartTime`. /// When set to 0, early access was not scheduled and started as soon as the price was set. uint32 earlyAccessStartTime; // 192-bits available in this slot /****** Slot 3 ******/ /// @notice Merkle roots representing which users have access to purchase during the early access period. /// @dev There may be many roots supported per sale where each is considered additive as any root may be used to /// purchase. mapping(bytes32 => bool) earlyAccessMerkleRoots; } /// @notice Stores the current sale information for all drop contracts. mapping(address => FixedPriceSaleConfig) private nftContractToFixedPriceSaleConfig; /** * @notice Emitted when an early access merkle root is added to a fixed price sale early access period. * @param nftContract The address of the NFT drop collection. * @param merkleRoot The merkleRoot used to authorize early access purchases. * @param merkleTreeUri The URI for the merkle tree represented by the merkleRoot. */ event AddMerkleRootToFixedPriceSale(address indexed nftContract, bytes32 merkleRoot, string merkleTreeUri); /** * @notice Emitted when a collection is listed for sale. * @param nftContract The address of the NFT drop collection. * @param seller The address for the seller which listed this for sale. * @param price The price per NFT minted. * @param limitPerAccount The max number of NFTs an account may mint in this sale. * @param generalAvailabilityStartTime The time at which general purchases are available, in seconds since Unix epoch. * Can not be more than two years from the creation block timestamp. * @param earlyAccessStartTime The time at which early access purchases are available, in seconds since Unix epoch. * Can not be more than two years from the creation block timestamp. * @param merkleRoot The merkleRoot used to authorize early access purchases, or 0 if n/a. * @param merkleTreeUri The URI for the merkle tree represented by the merkleRoot, or empty if n/a. */ event CreateFixedPriceSale( address indexed nftContract, address indexed seller, uint256 price, uint256 limitPerAccount, uint256 generalAvailabilityStartTime, uint256 earlyAccessStartTime, bytes32 merkleRoot, string merkleTreeUri ); /** * @notice Emitted when NFTs are minted from the drop. * @dev The total price paid by the buyer is `totalFees + creatorRev`. * @param nftContract The address of the NFT drop collection. * @param buyer The address of the buyer. * @param firstTokenId The tokenId for the first NFT minted. * The other minted tokens are assigned sequentially, so `firstTokenId` - `firstTokenId + count - 1` were minted. * @param count The number of NFTs minted. * @param totalFees The amount of ETH that was sent to Foundation & referrals for this sale. * @param creatorRev The amount of ETH that was sent to the creator for this sale. */ event MintFromFixedPriceDrop( address indexed nftContract, address indexed buyer, uint256 indexed firstTokenId, uint256 count, uint256 totalFees, uint256 creatorRev ); /** * @notice Add a merkle root to an existing fixed price sale early access period. * @param nftContract The address of the NFT drop collection. * @param merkleRoot The merkleRoot used to authorize early access purchases. * @param merkleTreeUri The URI for the merkle tree represented by the merkleRoot. * @dev If you accidentally pass in the wrong merkleTreeUri for a merkleRoot, * you can call this function again to emit a new event with a new merkleTreeUri. */ function addMerkleRootToFixedPriceSale( address nftContract, bytes32 merkleRoot, string calldata merkleTreeUri ) external notSoldOut(nftContract) onlyCollectionAdmin(nftContract) onlyValidMerkle(merkleRoot, merkleTreeUri) { FixedPriceSaleConfig storage saleConfig = nftContractToFixedPriceSaleConfig[nftContract]; if (saleConfig.generalAvailabilityStartTime.hasBeenReached()) { // Start time may be 0, check if this collection has been listed to provide a better error message. if (saleConfig.seller == payable(0)) { revert NFTDropMarketFixedPriceSale_Must_Be_Listed_For_Sale(); } // Adding users to the allow list is unnecessary when general access is open. revert NFTDropMarketFixedPriceSale_General_Access_Is_Open(); } if (saleConfig.generalAvailabilityStartTime == saleConfig.earlyAccessStartTime) { // Must have non-zero early access duration, otherwise merkle roots are unnecessary. revert NFTDropMarketFixedPriceSale_Must_Have_Non_Zero_Early_Access_Duration(); } saleConfig.earlyAccessMerkleRoots[merkleRoot] = true; emit AddMerkleRootToFixedPriceSale(nftContract, merkleRoot, merkleTreeUri); } /** * @notice [DEPRECATED] use `createFixedPriceSaleV3` instead. * Create a fixed price sale drop without an early access period. * @param nftContract The address of the NFT drop collection. * @param price The price per NFT minted. * Set price to 0 for a first come first serve airdrop-like drop. * @param limitPerAccount The max number of NFTs an account may mint in this sale. * @dev Notes: * a) The sale is final and can not be updated or canceled. * b) The sale is immediately kicked off. * c) Any collection that abides by `INFTLazyMintedCollectionMintCountTo` and `IAccessControl` is supported. */ function createFixedPriceSale(address nftContract, uint80 price, uint16 limitPerAccount) external { _createFixedPriceSale({ nftContract: nftContract, exhibitionId: 0, price: price, limitPerAccount: limitPerAccount, generalAvailabilityStartTime: block.timestamp, earlyAccessStartTime: block.timestamp, merkleRoot: bytes32(0), merkleTreeUri: "" }); } /** * @notice [DEPRECATED] use `createFixedPriceSaleV3` instead. * Create a fixed price sale drop without an early access period, * optionally scheduling the sale to start sometime in the future. * @param nftContract The address of the NFT drop collection. * @param price The price per NFT minted. * Set price to 0 for a first come first serve airdrop-like drop. * @param limitPerAccount The max number of NFTs an account may mint in this sale. * @param generalAvailabilityStartTime The time at which general purchases are available, in seconds since Unix epoch. * Set this to 0 in order to have general availability begin as soon as the transaction is mined. * Can not be more than two years from the creation block timestamp. * @param txDeadlineTime The deadline timestamp for the transaction to be mined, in seconds since Unix epoch. * Set this to 0 to send the transaction without a deadline. * @dev Notes: * a) The sale is final and can not be updated or canceled. * b) Any collection that abides by `INFTLazyMintedCollectionMintCountTo` and `IAccessControl` is supported. */ function createFixedPriceSaleV2( address nftContract, uint256 price, uint256 limitPerAccount, uint256 generalAvailabilityStartTime, uint256 txDeadlineTime ) external { createFixedPriceSaleV3({ nftContract: nftContract, exhibitionId: 0, price: price, limitPerAccount: limitPerAccount, generalAvailabilityStartTime: generalAvailabilityStartTime, txDeadlineTime: txDeadlineTime }); } /** * @notice Create a fixed price sale drop without an early access period, * optionally scheduling the sale to start sometime in the future. * @param nftContract The address of the NFT drop collection. * @param exhibitionId The exhibition to associate this fix priced sale to. * Set this to 0 to exist outside of an exhibition. * @param price The price per NFT minted. * Set price to 0 for a first come first serve airdrop-like drop. * @param limitPerAccount The max number of NFTs an account may mint in this sale. * @param generalAvailabilityStartTime The time at which general purchases are available, in seconds since Unix epoch. * Set this to 0 in order to have general availability begin as soon as the transaction is mined. * Can not be more than two years from the creation block timestamp. * @param txDeadlineTime The deadline timestamp for the transaction to be mined, in seconds since Unix epoch. * Set this to 0 to send the transaction without a deadline. * @dev Notes: * a) The sale is final and can not be updated or canceled. * b) Any collection that abides by `INFTLazyMintedCollectionMintCountTo` and `IAccessControl` is supported. */ function createFixedPriceSaleV3( address nftContract, uint256 exhibitionId, uint256 price, uint256 limitPerAccount, uint256 generalAvailabilityStartTime, uint256 txDeadlineTime ) public txDeadlineNotExpired(txDeadlineTime) { // When generalAvailabilityStartTime is not specified, default to now. if (generalAvailabilityStartTime == 0) { generalAvailabilityStartTime = block.timestamp; } else if (generalAvailabilityStartTime.hasExpired()) { // The start time must be now or in the future. revert NFTDropMarketFixedPriceSale_General_Availability_Start_Time_Has_Expired(); } _createFixedPriceSale({ nftContract: nftContract, exhibitionId: exhibitionId, price: price, limitPerAccount: limitPerAccount, generalAvailabilityStartTime: generalAvailabilityStartTime, earlyAccessStartTime: generalAvailabilityStartTime, merkleRoot: bytes32(0), merkleTreeUri: "" }); } /** * @notice [DEPRECATED] use `createFixedPriceSaleWithEarlyAccessAllowlistV2` instead. * Create a fixed price sale drop with an early access period. * @param nftContract The address of the NFT drop collection. * @param price The price per NFT minted. * Set price to 0 for a first come first serve airdrop-like drop. * @param limitPerAccount The max number of NFTs an account may mint in this sale. * @param generalAvailabilityStartTime The time at which general purchases are available, in seconds since Unix epoch. * This value must be > `earlyAccessStartTime`. * @param earlyAccessStartTime The time at which early access purchases are available, in seconds since Unix epoch. * Set this to 0 in order to have early access begin as soon as the transaction is mined. * @param merkleRoot The merkleRoot used to authorize early access purchases. * @param merkleTreeUri The URI for the merkle tree represented by the merkleRoot. * @param txDeadlineTime The deadline timestamp for the transaction to be mined, in seconds since Unix epoch. * Set this to 0 to send the transaction without a deadline. * @dev Notes: * a) The sale is final and can not be updated or canceled. * b) Any collection that abides by `INFTLazyMintedCollectionMintCountTo` and `IAccessControl` is supported. */ function createFixedPriceSaleWithEarlyAccessAllowlist( address nftContract, uint256 price, uint256 limitPerAccount, uint256 generalAvailabilityStartTime, uint256 earlyAccessStartTime, bytes32 merkleRoot, string calldata merkleTreeUri, uint256 txDeadlineTime ) external { createFixedPriceSaleWithEarlyAccessAllowlistV2({ nftContract: nftContract, exhibitionId: 0, price: price, limitPerAccount: limitPerAccount, generalAvailabilityStartTime: generalAvailabilityStartTime, earlyAccessStartTime: earlyAccessStartTime, merkleRoot: merkleRoot, merkleTreeUri: merkleTreeUri, txDeadlineTime: txDeadlineTime }); } /** * @notice Create a fixed price sale drop with an early access period. * @param nftContract The address of the NFT drop collection. * @param exhibitionId The exhibition to associate this fix priced sale to. * Set this to 0 to exist outside of an exhibition. * @param price The price per NFT minted. * Set price to 0 for a first come first serve airdrop-like drop. * @param limitPerAccount The max number of NFTs an account may mint in this sale. * @param generalAvailabilityStartTime The time at which general purchases are available, in seconds since Unix epoch. * This value must be > `earlyAccessStartTime`. * @param earlyAccessStartTime The time at which early access purchases are available, in seconds since Unix epoch. * Set this to 0 in order to have early access begin as soon as the transaction is mined. * @param merkleRoot The merkleRoot used to authorize early access purchases. * @param merkleTreeUri The URI for the merkle tree represented by the merkleRoot. * @param txDeadlineTime The deadline timestamp for the transaction to be mined, in seconds since Unix epoch. * Set this to 0 to send the transaction without a deadline. * @dev Notes: * a) The sale is final and can not be updated or canceled. * b) Any collection that abides by `INFTLazyMintedCollectionMintCountTo` and `IAccessControl` is supported. */ function createFixedPriceSaleWithEarlyAccessAllowlistV2( address nftContract, uint256 exhibitionId, uint256 price, uint256 limitPerAccount, uint256 generalAvailabilityStartTime, uint256 earlyAccessStartTime, bytes32 merkleRoot, string calldata merkleTreeUri, uint256 txDeadlineTime ) public txDeadlineNotExpired(txDeadlineTime) onlyValidMerkle(merkleRoot, merkleTreeUri) { // When earlyAccessStartTime is not specified, default to now. if (earlyAccessStartTime == 0) { earlyAccessStartTime = block.timestamp; } else if (earlyAccessStartTime.hasExpired()) { // The start time must be now or in the future. revert NFTDropMarketFixedPriceSale_Early_Access_Start_Time_Has_Expired(); } if (earlyAccessStartTime >= generalAvailabilityStartTime) { // Early access period must start before GA period. revert NFTDropMarketFixedPriceSale_Must_Have_Non_Zero_Early_Access_Duration(); } _createFixedPriceSale( nftContract, exhibitionId, price, limitPerAccount, generalAvailabilityStartTime, earlyAccessStartTime, merkleRoot, merkleTreeUri ); } /** * @notice Used to mint `count` number of NFTs from the collection during general availability. * @param nftContract The address of the NFT drop collection. * @param count The number of NFTs to mint. * @param buyReferrer The address which referred this purchase, or address(0) if n/a. * @return firstTokenId The tokenId for the first NFT minted. * The other minted tokens are assigned sequentially, so `firstTokenId` - `firstTokenId + count - 1` were minted. * @dev This call may revert if the collection has sold out, has an insufficient number of tokens available, * or if the market's minter permissions were removed. * If insufficient msg.value is included, the sender's available FETH token balance will be used. */ function mintFromFixedPriceSale( address nftContract, uint16 count, address payable buyReferrer ) external payable returns (uint256 firstTokenId) { FixedPriceSaleConfig storage saleConfig = nftContractToFixedPriceSaleConfig[nftContract]; // Must be in general access period. if (!saleConfig.generalAvailabilityStartTime.hasBeenReached()) { revert NFTDropMarketFixedPriceSale_General_Access_Not_Open(saleConfig.generalAvailabilityStartTime); } firstTokenId = _mintFromFixedPriceSale(saleConfig, nftContract, count, buyReferrer); } /** * @notice Used to mint `count` number of NFTs from the collection during early access. * @param nftContract The address of the NFT drop collection. * @param count The number of NFTs to mint. * @param buyReferrer The address which referred this purchase, or address(0) if n/a. * @param proof The merkle proof used to authorize this purchase. * @return firstTokenId The tokenId for the first NFT minted. * The other minted tokens are assigned sequentially, so `firstTokenId` - `firstTokenId + count - 1` were minted. * @dev This call may revert if the collection has sold out, has an insufficient number of tokens available, * or if the market's minter permissions were removed. * If insufficient msg.value is included, the sender's available FETH token balance will be used. */ function mintFromFixedPriceSaleWithEarlyAccessAllowlist( address nftContract, uint256 count, address payable buyReferrer, bytes32[] calldata proof ) external payable returns (uint256 firstTokenId) { FixedPriceSaleConfig storage saleConfig = nftContractToFixedPriceSaleConfig[nftContract]; // Skip proof check if in general access period. if (!saleConfig.generalAvailabilityStartTime.hasBeenReached()) { // Must be in early access period or beyond. if (!saleConfig.earlyAccessStartTime.hasBeenReached()) { if (saleConfig.earlyAccessStartTime == saleConfig.generalAvailabilityStartTime) { // This just provides a more targeted error message for the case where early access is not enabled. revert NFTDropMarketFixedPriceSale_Must_Have_Non_Zero_Early_Access_Duration(); } revert NFTDropMarketFixedPriceSale_Early_Access_Not_Open(saleConfig.earlyAccessStartTime); } bytes32 root = _msgSender().getMerkleRootForAddress(proof); if (!saleConfig.earlyAccessMerkleRoots[root]) { revert NFTDropMarketFixedPriceSale_Invalid_Merkle_Proof(); } } firstTokenId = _mintFromFixedPriceSale(saleConfig, nftContract, count, buyReferrer); } function _createFixedPriceSale( address nftContract, uint256 exhibitionId, uint256 price, uint256 limitPerAccount, uint256 generalAvailabilityStartTime, uint256 earlyAccessStartTime, bytes32 merkleRoot, string memory merkleTreeUri ) private onlySupportedCollectionType(nftContract) notSoldOut(nftContract) onlyCollectionAdmin(nftContract) onlyValidScheduledTime(generalAvailabilityStartTime) notListed(nftContract) { // Validate input params. if (limitPerAccount == 0) { // A non-zero limit is required. revert NFTDropMarketFixedPriceSale_Limit_Per_Account_Must_Be_Set(); } // Confirm this collection has not already been listed. FixedPriceSaleConfig storage saleConfig = nftContractToFixedPriceSaleConfig[nftContract]; // Save the sale details. address payable sender = payable(_msgSender()); saleConfig.seller = sender; // Any price is supported, including 0. saleConfig.price = price.toUint80(); saleConfig.limitPerAccount = limitPerAccount.toUint16(); if (generalAvailabilityStartTime != block.timestamp) { // If starting now we don't need to write to storage // Safe cast is not required since onlyValidScheduledTime confirms the max is within range. saleConfig.generalAvailabilityStartTime = uint32(generalAvailabilityStartTime); } if (earlyAccessStartTime != block.timestamp) { // If starting now we don't need to write to storage // Safe cast is not required since callers require earlyAccessStartTime <= generalAvailabilityStartTime. saleConfig.earlyAccessStartTime = uint32(earlyAccessStartTime); } // Store the merkle root if there's an early access period if (merkleRoot != 0) { saleConfig.earlyAccessMerkleRoots[merkleRoot] = true; } _addCollectionToExhibition(nftContract, exhibitionId); emit CreateFixedPriceSale({ nftContract: nftContract, seller: sender, price: price, limitPerAccount: limitPerAccount, generalAvailabilityStartTime: generalAvailabilityStartTime, earlyAccessStartTime: earlyAccessStartTime, merkleRoot: merkleRoot, merkleTreeUri: merkleTreeUri }); } function _mintFromFixedPriceSale( FixedPriceSaleConfig storage saleConfig, address nftContract, uint256 count, address payable buyReferrer ) private returns (uint256 firstTokenId) { // Validate input params. if (count == 0) { revert NFTDropMarketFixedPriceSale_Must_Buy_At_Least_One_Token(); } // Confirm that the buyer will not exceed the limit specified after minting. address sender = _msgSender(); uint256 minted = saleConfig.userToMintedCount[sender] + count; if (minted > saleConfig.limitPerAccount) { if (saleConfig.limitPerAccount == 0) { // Provide a more targeted error if the collection has not been listed. revert NFTDropMarketFixedPriceSale_Must_Be_Listed_For_Sale(); } revert NFTDropMarketFixedPriceSale_Cannot_Buy_More_Than_Limit(saleConfig.limitPerAccount); } saleConfig.userToMintedCount[sender] = minted; // Calculate the total cost, considering the `count` requested. uint256 mintCost; unchecked { // Can not overflow as 2^80 * 2^16 == 2^96 max which fits in 256 bits. mintCost = uint256(saleConfig.price) * count; } // The sale price is immutable so the buyer is aware of how much they will be paying when their tx is broadcasted. if (msg.value > mintCost) { // Since price is known ahead of time, if too much ETH is sent then something went wrong. revert NFTDropMarketFixedPriceSale_Too_Much_Value_Provided(mintCost); } // Withdraw from the user's available FETH balance if insufficient msg.value was included. _tryUseFETHBalance({ totalAmount: mintCost, shouldRefundSurplus: false }); // Mint the NFTs. // Safe cast is not required, above confirms count <= limitPerAccount which is uint16. firstTokenId = INFTLazyMintedCollectionMintCountTo(nftContract).mintCountTo(uint16(count), sender); (address payable curator, uint16 takeRateInBasisPoints) = _getExhibitionByCollection(nftContract); // Distribute revenue from this sale. (uint256 totalFees, uint256 creatorRev, ) = _distributeFunds({ nftContract: nftContract, tokenId: firstTokenId, seller: saleConfig.seller, price: mintCost, buyReferrer: buyReferrer, sellerReferrerPaymentAddress: curator, sellerReferrerTakeRateInBasisPoints: takeRateInBasisPoints }); emit MintFromFixedPriceDrop({ nftContract: nftContract, buyer: sender, firstTokenId: firstTokenId, count: count, totalFees: totalFees, creatorRev: creatorRev }); } /** * @notice Returns the max number of NFTs a given account may mint. * @param nftContract The address of the NFT drop collection. * @param user The address of the user which will be minting. * @return numberThatCanBeMinted How many NFTs the user can mint. */ function getAvailableCountFromFixedPriceSale( address nftContract, address user ) external view returns (uint256 numberThatCanBeMinted) { (, , uint256 limitPerAccount, uint256 numberOfTokensAvailableToMint, bool marketCanMint, , ) = getFixedPriceSale( nftContract ); if (!marketCanMint) { // No one can mint in the current state. return 0; } uint256 mintedCount = nftContractToFixedPriceSaleConfig[nftContract].userToMintedCount[user]; if (mintedCount >= limitPerAccount) { // User has exhausted their limit. return 0; } unchecked { // Safe math is not required due to the if statement directly above. numberThatCanBeMinted = limitPerAccount - mintedCount; } if (numberThatCanBeMinted > numberOfTokensAvailableToMint) { // User has more tokens available than the collection has available. numberThatCanBeMinted = numberOfTokensAvailableToMint; } } /** * @notice Returns details for a drop collection's fixed price sale. * @param nftContract The address of the NFT drop collection. * @return seller The address of the seller which listed this drop for sale. * This value will be address(0) if the collection is not listed or has sold out. * @return price The price per NFT minted. * @return limitPerAccount The max number of NFTs an account may mint in this sale. * @return numberOfTokensAvailableToMint The number of NFTs available to mint. * @return marketCanMint True if this contract has permissions to mint from the given collection. * @return generalAvailabilityStartTime The time at which general availability starts. * When set to 0, general availability was not scheduled and started as soon as the price was set. * @return earlyAccessStartTime The timestamp at which the allowlist period starts. * When set to 0, early access was not scheduled and started as soon as the price was set. */ function getFixedPriceSale( address nftContract ) public view returns ( address payable seller, uint256 price, uint256 limitPerAccount, uint256 numberOfTokensAvailableToMint, bool marketCanMint, uint256 generalAvailabilityStartTime, uint256 earlyAccessStartTime ) { try INFTLazyMintedCollectionMintCountTo(nftContract).numberOfTokensAvailableToMint() returns (uint256 count) { if (count != 0) { try IAccessControl(nftContract).hasRole(MINTER_ROLE, address(this)) returns (bool hasRole) { FixedPriceSaleConfig storage saleConfig = nftContractToFixedPriceSaleConfig[nftContract]; seller = saleConfig.seller; price = saleConfig.price; limitPerAccount = saleConfig.limitPerAccount; numberOfTokensAvailableToMint = count; marketCanMint = hasRole; earlyAccessStartTime = saleConfig.earlyAccessStartTime; generalAvailabilityStartTime = saleConfig.generalAvailabilityStartTime; } catch { // The contract is not supported - return default values. } } // Else minted completed -- return default values. } catch { // Contract not supported or self destructed - return default values } } /** * @notice Checks if a given merkle root has been authorized to purchase from a given drop collection. * @param nftContract The address of the NFT drop collection. * @param merkleRoot The merkle root to check. * @return supported True if the merkle root has been authorized. */ function getFixedPriceSaleEarlyAccessAllowlistSupported( address nftContract, bytes32 merkleRoot ) external view returns (bool supported) { supported = nftContractToFixedPriceSaleConfig[nftContract].earlyAccessMerkleRoots[merkleRoot]; } /** * @inheritdoc MarketSharedCore * @dev Returns the seller for a collection if listed and not already sold out. */ function _getSellerOf( address nftContract, uint256 tokenId ) internal view virtual override returns (address payable seller) { (seller, , , , , , ) = getFixedPriceSale(nftContract); if (seller == address(0)) { seller = super._getSellerOf(nftContract, tokenId); } } /** * @inheritdoc NFTDropMarketCore */ function _isCollectionListed(address nftContract) internal virtual override returns (bool isListed) { isListed = nftContractToFixedPriceSaleConfig[nftContract].seller != address(0) || super._isCollectionListed(nftContract); } /** * @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[1_000] private __gap; }
// 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_EXHIBITION_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 10%, expressed as a denominator for more efficient calculations. */ uint256 constant ROYALTY_RATIO = BASIS_POINTS / ROYALTY_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 edition collection type. */ string constant NFT_TIMED_EDITION_COLLECTION_TYPE = "NFT Timed 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 name. * This is precalculated in order to save gas on use. * `keccak256(abi.encodePacked(NFT_TIMED_EDITION_COLLECTION_TYPE))` */ bytes32 constant editionTypeHash = 0xee2afa3f960e108aca17013728aafa363a0f4485661d9b6f41c6b4ddb55008ee;
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/Context.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 Context { 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); } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; 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 A mixin that stores a reference to the Foundation treasury contract. * @notice The treasury collects fees and defines admin/operator roles. * @author batu-inal & HardlyDifficult */ abstract contract FoundationTreasuryNode { 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 */ uint256[2_000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @title A placeholder contract leaving room for new mixins to be added to the future. * @author HardlyDifficult */ abstract contract Gap1000 { /** * @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[1_000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; /** * @title A placeholder contract leaving room for new mixins to be added to the future. * @author HardlyDifficult */ abstract contract Gap8500 { /** * @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[8_500] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import "@manifoldxyz/royalty-registry-solidity/contracts/IRoyaltyRegistry.sol"; import "../../interfaces/internal/INFTCollectionType.sol"; import "../../interfaces/standards/royalties/IGetFees.sol"; import "../../interfaces/standards/royalties/IGetRoyalties.sol"; import "../../interfaces/standards/royalties/IOwnable.sol"; import "../../interfaces/standards/royalties/IRoyaltyInfo.sol"; import "../../interfaces/standards/royalties/ITokenCreator.sol"; import "../../libraries/ArrayLibrary.sol"; import "./Constants.sol"; import "./FoundationTreasuryNode.sol"; import "./SendValueWithFallbackWithdraw.sol"; import "./MarketSharedCore.sol"; error NFTMarketFees_Royalty_Registry_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 FoundationTreasuryNode, Context, MarketSharedCore, SendValueWithFallbackWithdraw { using AddressUpgradeable for address; using ArrayLibrary for address payable[]; using ArrayLibrary for uint256[]; using ERC165Checker 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 The royalties sent to creator recipients on secondary sales. uint256 private constant CREATOR_ROYALTY_DENOMINATOR = BASIS_POINTS / 1_000; // 10% /// @notice The fee collected by Foundation for sales facilitated by this market contract. uint256 private immutable DEFAULT_PROTOCOL_FEE_IN_BASIS_POINTS; /// @notice The fee collected by the buy referrer for sales facilitated by this market contract. /// This fee is calculated from the total protocol fee. uint256 private constant BUY_REFERRER_FEE_DENOMINATOR = BASIS_POINTS / 100; // 1% /// @notice The address of the royalty registry which may be used to define royalty overrides for some collections. IRoyaltyRegistry private immutable royaltyRegistry; /// @notice The address of this contract's implementation. /// @dev This is used when making stateless external calls to this contract, /// saving gas over hopping through the proxy which is only necessary when accessing state. MarketFees private immutable implementationAddress; /// @notice True for the Drop market which only performs primary sales. False if primary & secondary are supported. bool private immutable assumePrimarySale; /** * @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 Configures the registry allowing for royalty overrides to be defined. * @param _royaltyRegistry The registry to use for royalty overrides. * @param _assumePrimarySale True for the Drop market which only performs primary sales. * False if primary & secondary are supported. */ constructor(uint16 protocolFeeInBasisPoints, address _royaltyRegistry, bool _assumePrimarySale) { if ( protocolFeeInBasisPoints < BASIS_POINTS / BUY_REFERRER_FEE_DENOMINATOR || protocolFeeInBasisPoints + BASIS_POINTS / CREATOR_ROYALTY_DENOMINATOR >= BASIS_POINTS - MAX_EXHIBITION_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(); } DEFAULT_PROTOCOL_FEE_IN_BASIS_POINTS = protocolFeeInBasisPoints; if (!_royaltyRegistry.isContract()) { // Not using a 165 check since mainnet and goerli are not using the same versions of the registry. revert NFTMarketFees_Royalty_Registry_Is_Not_A_Contract(); } royaltyRegistry = IRoyaltyRegistry(_royaltyRegistry); assumePrimarySale = _assumePrimarySale; // In the constructor, `this` refers to the implementation address. Everywhere else it'll be the proxy. implementationAddress = this; } /** * @notice Distributes funds to foundation, creator recipients, and NFT owner after a sale. */ function _distributeFunds( address nftContract, uint256 tokenId, address payable seller, uint256 price, address payable buyReferrer, address payable sellerReferrerPaymentAddress, uint16 sellerReferrerTakeRateInBasisPoints ) internal 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 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)`. */ 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 registry allowing for royalty configuration overrides. * @dev See https://royaltyregistry.xyz/ * @return registry The address of the royalty registry contract. */ function getRoyaltyRegistry() external view returns (address registry) { registry = address(royaltyRegistry); } /** * @notice **For internal use only.** * @dev This function is external to allow using try/catch but is not intended for external use. * This checks the token creator. */ function internalGetTokenCreator( address nftContract, uint256 tokenId ) external view returns (address payable creator) { creator = ITokenCreator(nftContract).tokenCreator{ gas: READ_ONLY_GAS_LIMIT }(tokenId); } /** * @notice **For internal use only.** * @dev This function is external to allow using try/catch but is not intended for external use. * If ERC2981 royalties (or getRoyalties) are defined by the NFT contract, allow this standard to define immutable * royalties that cannot be later changed via the royalty registry. */ function internalGetImmutableRoyalties( address nftContract, uint256 tokenId ) external view returns (address payable[] memory recipients, uint256[] memory splitPerRecipientInBasisPoints) { // 1st priority: ERC-2981 if (nftContract.supportsERC165InterfaceUnchecked(type(IRoyaltyInfo).interfaceId)) { try IRoyaltyInfo(nftContract).royaltyInfo{ gas: READ_ONLY_GAS_LIMIT }(tokenId, BASIS_POINTS) returns ( address receiver, uint256 royaltyAmount ) { // Manifold contracts return (address(this), 0) when royalties are not defined // - so ignore results when the amount is 0 if (royaltyAmount > 0) { recipients = new address payable[](1); recipients[0] = payable(receiver); splitPerRecipientInBasisPoints = new uint256[](1); // The split amount is assumed to be 100% when only 1 recipient is returned return (recipients, splitPerRecipientInBasisPoints); } } catch { // Fall through } } // 2nd priority: getRoyalties if (nftContract.supportsERC165InterfaceUnchecked(type(IGetRoyalties).interfaceId)) { try IGetRoyalties(nftContract).getRoyalties{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( address payable[] memory _recipients, uint256[] memory recipientBasisPoints ) { if (_recipients.length != 0 && _recipients.length == recipientBasisPoints.length) { return (_recipients, recipientBasisPoints); } } catch { // Fall through } } } /** * @notice **For internal use only.** * @dev This function is external to allow using try/catch but is not intended for external use. * This checks for royalties defined in the royalty registry or via a non-standard royalty API. */ function internalGetMutableRoyalties( address nftContract, uint256 tokenId, address payable creator ) external view returns (address payable[] memory recipients, uint256[] memory splitPerRecipientInBasisPoints) { /* Overrides must support ERC-165 when registered, except for overrides defined by the registry owner. If that results in an override w/o 165 we may need to upgrade the market to support or ignore that override. */ // The registry requires overrides are not 0 and contracts when set. // If no override is set, the nftContract address is returned. try royaltyRegistry.getRoyaltyLookupAddress{ gas: READ_ONLY_GAS_LIMIT }(nftContract) returns ( address overrideContract ) { if (overrideContract != nftContract) { nftContract = overrideContract; // The functions above are repeated here if an override is set. // 3rd priority: ERC-2981 override if (nftContract.supportsERC165InterfaceUnchecked(type(IRoyaltyInfo).interfaceId)) { try IRoyaltyInfo(nftContract).royaltyInfo{ gas: READ_ONLY_GAS_LIMIT }(tokenId, BASIS_POINTS) returns ( address receiver, uint256 royaltyAmount ) { // Manifold contracts return (address(this), 0) when royalties are not defined // - so ignore results when the amount is 0 if (royaltyAmount != 0) { recipients = new address payable[](1); recipients[0] = payable(receiver); splitPerRecipientInBasisPoints = new uint256[](1); // The split amount is assumed to be 100% when only 1 recipient is returned return (recipients, splitPerRecipientInBasisPoints); } } catch { // Fall through } } // 4th priority: getRoyalties override if (recipients.length == 0 && nftContract.supportsERC165InterfaceUnchecked(type(IGetRoyalties).interfaceId)) { try IGetRoyalties(nftContract).getRoyalties{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( address payable[] memory _recipients, uint256[] memory recipientBasisPoints ) { if (_recipients.length != 0 && _recipients.length == recipientBasisPoints.length) { return (_recipients, recipientBasisPoints); } } catch { // Fall through } } } } catch { // Ignore out of gas errors and continue using the nftContract address } // 5th priority: getFee* from contract or override if (nftContract.supportsERC165InterfaceUnchecked(type(IGetFees).interfaceId)) { try IGetFees(nftContract).getFeeRecipients{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( address payable[] memory _recipients ) { if (_recipients.length != 0) { try IGetFees(nftContract).getFeeBps{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( uint256[] memory recipientBasisPoints ) { if (_recipients.length == recipientBasisPoints.length) { return (_recipients, recipientBasisPoints); } } catch { // Fall through } } } catch { // Fall through } } // 6th priority: tokenCreator w/ or w/o requiring 165 from contract or override if (creator != address(0)) { // Only pay the tokenCreator if there wasn't another royalty defined recipients = new address payable[](1); recipients[0] = creator; splitPerRecipientInBasisPoints = new uint256[](1); // The split amount is assumed to be 100% when only 1 recipient is returned return (recipients, splitPerRecipientInBasisPoints); } // 7th priority: owner from contract or override try IOwnable(nftContract).owner{ gas: READ_ONLY_GAS_LIMIT }() returns (address owner) { if (owner != address(0)) { // Only pay the owner if there wasn't another royalty defined recipients = new address payable[](1); recipients[0] = payable(owner); splitPerRecipientInBasisPoints = new uint256[](1); // The split amount is assumed to be 100% when only 1 recipient is returned return (recipients, splitPerRecipientInBasisPoints); } } catch { // Fall through } // If no valid payment address or creator is found, return 0 recipients } /** * @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 ) private view returns ( uint256 totalFees, address payable[] memory creatorRecipients, uint256[] memory creatorShares, uint256 sellerRev, uint256 buyReferrerFee, uint256 sellerReferrerFee ) { // Calculate the protocol fee totalFees = (price * _getProtocolFee(nftContract)) / BASIS_POINTS; address payable creator; try implementationAddress.internalGetTokenCreator(nftContract, tokenId) returns (address payable _creator) { creator = _creator; } catch { // Fall through } try implementationAddress.internalGetImmutableRoyalties(nftContract, tokenId) returns ( address payable[] memory _recipients, uint256[] memory _splitPerRecipientInBasisPoints ) { (creatorRecipients, creatorShares) = (_recipients, _splitPerRecipientInBasisPoints); } catch { // Fall through } if (creatorRecipients.length == 0) { // Check mutable royalties only if we didn't find results from the immutable API try implementationAddress.internalGetMutableRoyalties(nftContract, tokenId, creator) returns ( address payable[] memory _recipients, uint256[] memory _splitPerRecipientInBasisPoints ) { (creatorRecipients, creatorShares) = (_recipients, _splitPerRecipientInBasisPoints); } catch { // Fall through } } if (creatorRecipients.length != 0 || assumePrimarySale) { uint256 creatorRev; if (assumePrimarySale) { // All revenue should go to the creator recipients unchecked { // totalFees is always < price. creatorRev = price - totalFees; } if (creatorRecipients.length == 0) { // If no creators were found via the royalty APIs, then set that recipient to the seller's address creatorRecipients = new address payable[](1); creatorRecipients[0] = seller; creatorShares = new uint256[](1); // The split amount is assumed to be 100% when only 1 recipient is returned } } else if (seller == creator || (creatorRecipients.length != 0 && seller == creatorRecipients[0])) { // When sold by the creator, all revenue is split if applicable. unchecked { // totalFees is always < price. creatorRev = price - totalFees; } } else { // Rounding favors the owner first, then creator, and foundation last. unchecked { // Safe math is not required when dividing by a non-zero constant. creatorRev = price / CREATOR_ROYALTY_DENOMINATOR; } sellerRev = price - totalFees - creatorRev; } // Cap the max number of recipients supported creatorRecipients.capLength(MAX_ROYALTY_RECIPIENTS); creatorShares.capLength(MAX_ROYALTY_RECIPIENTS); // Calculate the seller referrer fee when some revenue is awarded to the creator if (sellerReferrerTakeRateInBasisPoints != 0) { sellerReferrerFee = (price * sellerReferrerTakeRateInBasisPoints) / BASIS_POINTS; // Subtract the seller referrer fee from the seller revenue so we do not double pay. if (sellerRev == 0) { // If the seller revenue is 0, this is a primary sale where all seller revenue is attributed to the "creator". creatorRev -= sellerReferrerFee; } else { sellerRev -= sellerReferrerFee; } } // Sum the total shares defined uint256 totalShares; if (creatorRecipients.length > 1) { unchecked { for (uint256 i = 0; i < creatorRecipients.length; ++i) { if (creatorRecipients[i] == seller) { // If the seller is any of the recipients defined, assume a primary sale creatorRev += sellerRev; sellerRev = 0; } if (totalShares != type(uint256).max) { if (creatorShares[i] > BASIS_POINTS) { // If the numbers are >100% we ignore the fee recipients and pay just the first instead totalShares = type(uint256).max; // Continue the loop in order to detect a potential primary sale condition } else { totalShares += creatorShares[i]; } } } } if (totalShares == 0 || totalShares == type(uint256).max) { // If no shares were defined or shares were out of bounds, pay only the first recipient creatorRecipients.capLength(1); creatorShares.capLength(1); } } // Send payouts to each additional recipient if more than 1 was defined uint256 totalRoyaltiesDistributed; for (uint256 i = 1; i < creatorRecipients.length; ) { uint256 royalty = (creatorRev * creatorShares[i]) / totalShares; totalRoyaltiesDistributed += royalty; creatorShares[i] = royalty; unchecked { ++i; } } // Send the remainder to the 1st creator, rounding in their favor creatorShares[0] = creatorRev - totalRoyaltiesDistributed; } else { // No royalty recipients found. unchecked { // totalFees is always < price. sellerRev = price - totalFees; } // Calculate the seller referrer fee when there is no creator royalty if (sellerReferrerTakeRateInBasisPoints != 0) { sellerReferrerFee = (price * sellerReferrerTakeRateInBasisPoints) / BASIS_POINTS; sellerRev -= sellerReferrerFee; } } if (buyReferrer != address(0) && buyReferrer != _msgSender() && buyReferrer != seller && buyReferrer != creator) { unchecked { buyReferrerFee = price / BUY_REFERRER_FEE_DENOMINATOR; // buyReferrerFee is always <= totalFees totalFees -= buyReferrerFee; } } } /** * @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 = DEFAULT_PROTOCOL_FEE_IN_BASIS_POINTS; } /** * @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[500] private __gap; }
// 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[500] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; error RouterContext_Not_A_Contract(); /** * @title Enables a trusted router contract to override the usual msg.sender address. * @author HardlyDifficult */ abstract contract RouterContext is Context { using AddressUpgradeable for address; address private immutable approvedRouter; constructor(address router) { if (!router.isContract()) { revert RouterContext_Not_A_Contract(); } approvedRouter = router; } /** * @notice Returns the router contract which is able to override the msg.sender address. * @return router The address of the trusted router. */ function getApprovedRouterAddress() external view returns (address router) { router = approvedRouter; } /** * @notice Returns the sender of the transaction. * @dev If the msg.sender is the trusted router contract, then the last 20 bytes of the calldata is the authorized * sender. */ function _msgSender() internal view virtual override returns (address sender) { sender = super._msgSender(); if (sender == approvedRouter) { 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))) } } } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.18; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "./FETHNode.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 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; } // 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; }
// 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. }
{ "optimizer": { "enabled": true, "runs": 25000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address payable","name":"treasury","type":"address"},{"internalType":"address","name":"feth","type":"address"},{"internalType":"address","name":"royaltyRegistry","type":"address"},{"internalType":"address","name":"nftMarket","type":"address"},{"internalType":"address","name":"router","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FETHNode_FETH_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"FETHNode_Only_FETH_Can_Transfer_ETH","type":"error"},{"inputs":[],"name":"FoundationTreasuryNode_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTDropMarketCore_Collection_Already_Listed_For_Sale","type":"error"},{"inputs":[],"name":"NFTDropMarketCore_Invalid_Merkle_Root","type":"error"},{"inputs":[],"name":"NFTDropMarketCore_Invalid_Merkle_Tree_URI","type":"error"},{"inputs":[],"name":"NFTDropMarketCore_Mint_Permission_Required","type":"error"},{"inputs":[],"name":"NFTDropMarketCore_Must_Have_Available_Supply","type":"error"},{"inputs":[],"name":"NFTDropMarketCore_Must_Support_Collection_Mint_Interface","type":"error"},{"inputs":[],"name":"NFTDropMarketCore_Must_Support_ERC721","type":"error"},{"inputs":[],"name":"NFTDropMarketCore_Only_Callable_By_Collection_Owner","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxTime","type":"uint256"}],"name":"NFTDropMarketCore_Time_Too_Far_In_The_Future","type":"error"},{"inputs":[{"internalType":"uint256","name":"startTime","type":"uint256"}],"name":"NFTDropMarketDutchAuction_Auction_Has_Not_Started_Yet","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Auction_Not_Found","type":"error"},{"inputs":[{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"numberStillAvailable","type":"uint256"}],"name":"NFTDropMarketDutchAuction_Clearing_Price_Not_Reached","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Creator_Revenue_Has_Already_Been_Withdrawn","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxTime","type":"uint256"}],"name":"NFTDropMarketDutchAuction_End_Time_Too_Far_In_The_Future","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Limit_Per_Account_Must_Be_Set","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Min_Price_Must_Be_Less_Than_Max_Price","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Mint_Count_Must_Be_Greater_Than_Zero","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentMintCount","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"}],"name":"NFTDropMarketDutchAuction_Mint_Exceeds_Limit_Per_Account","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Must_Have_Available_Supply","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Nothing_To_Rebate_At_This_Time","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Sale_Duration_Must_Be_Greater_Than_Zero","type":"error"},{"inputs":[],"name":"NFTDropMarketDutchAuction_Start_Time_Must_Not_Be_In_The_Past","type":"error"},{"inputs":[],"name":"NFTDropMarketExhibition_Exhibition_Does_Not_Exist","type":"error"},{"inputs":[],"name":"NFTDropMarketExhibition_NFT_Market_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTDropMarketExhibition_Seller_Not_Allowed_In_Exhibition","type":"error"},{"inputs":[{"internalType":"uint256","name":"limitPerAccount","type":"uint256"}],"name":"NFTDropMarketFixedPriceSale_Cannot_Buy_More_Than_Limit","type":"error"},{"inputs":[{"internalType":"uint256","name":"earlyAccessStartTime","type":"uint256"}],"name":"NFTDropMarketFixedPriceSale_Early_Access_Not_Open","type":"error"},{"inputs":[],"name":"NFTDropMarketFixedPriceSale_Early_Access_Start_Time_Has_Expired","type":"error"},{"inputs":[],"name":"NFTDropMarketFixedPriceSale_General_Access_Is_Open","type":"error"},{"inputs":[{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"}],"name":"NFTDropMarketFixedPriceSale_General_Access_Not_Open","type":"error"},{"inputs":[],"name":"NFTDropMarketFixedPriceSale_General_Availability_Start_Time_Has_Expired","type":"error"},{"inputs":[],"name":"NFTDropMarketFixedPriceSale_Invalid_Merkle_Proof","type":"error"},{"inputs":[],"name":"NFTDropMarketFixedPriceSale_Limit_Per_Account_Must_Be_Set","type":"error"},{"inputs":[],"name":"NFTDropMarketFixedPriceSale_Must_Be_Listed_For_Sale","type":"error"},{"inputs":[],"name":"NFTDropMarketFixedPriceSale_Must_Buy_At_Least_One_Token","type":"error"},{"inputs":[],"name":"NFTDropMarketFixedPriceSale_Must_Have_Non_Zero_Early_Access_Duration","type":"error"},{"inputs":[{"internalType":"uint256","name":"mintCost","type":"uint256"}],"name":"NFTDropMarketFixedPriceSale_Too_Much_Value_Provided","type":"error"},{"inputs":[],"name":"NFTDropMarket_NFT_Already_Minted","type":"error"},{"inputs":[],"name":"NFTMarketFees_Invalid_Protocol_Fee","type":"error"},{"inputs":[],"name":"NFTMarketFees_Royalty_Registry_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"RouterContext_Not_A_Contract","type":"error"},{"inputs":[],"name":"TxDeadline_Tx_Deadline_Expired","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":false,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"indexed":false,"internalType":"string","name":"merkleTreeUri","type":"string"}],"name":"AddMerkleRootToFixedPriceSale","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyReferrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyReferrerFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"buyReferrerSellerFee","type":"uint256"}],"name":"BuyReferralPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"name":"CollectionAddedToExhibition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"earlyAccessStartTime","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"indexed":false,"internalType":"string","name":"merkleTreeUri","type":"string"}],"name":"CreateFixedPriceSale","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"maxPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"CreateLinearDutchAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"pricePaidPerNft","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"firstTokenId","type":"uint256"}],"name":"MintFromDutchAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"uint256","name":"firstTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorRev","type":"uint256"}],"name":"MintFromFixedPriceDrop","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"rebate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"currentPricePerNft","type":"uint256"}],"name":"RebateBuyerFromDutchAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"sellerReferrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"sellerReferrerFee","type":"uint256"}],"name":"SellerReferralPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"clearingPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalMintedCount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorRev","type":"uint256"}],"name":"WithdrawCreatorRevenueFromDutchAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawalToFETH","type":"event"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"string","name":"merkleTreeUri","type":"string"}],"name":"addMerkleRootToFixedPriceSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint80","name":"price","type":"uint80"},{"internalType":"uint16","name":"limitPerAccount","type":"uint16"}],"name":"createFixedPriceSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"},{"internalType":"uint256","name":"txDeadlineTime","type":"uint256"}],"name":"createFixedPriceSaleV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"exhibitionId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"},{"internalType":"uint256","name":"txDeadlineTime","type":"uint256"}],"name":"createFixedPriceSaleV3","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"},{"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":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"exhibitionId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"},{"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":"createFixedPriceSaleWithEarlyAccessAllowlistV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"exhibitionId","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":[],"name":"getApprovedRouterAddress","outputs":[{"internalType":"address","name":"router","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"user","type":"address"}],"name":"getAvailableCountFromFixedPriceSale","outputs":[{"internalType":"uint256","name":"numberThatCanBeMinted","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address","name":"buyer","type":"address"}],"name":"getBuyerInfoFromDutchAuction","outputs":[{"internalType":"uint256","name":"outstandingRebateBalance","type":"uint256"},{"internalType":"uint256","name":"numberThatCanBeMinted","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getDutchAuction","outputs":[{"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":"endTime","type":"uint256"},{"internalType":"uint256","name":"totalAvailableSupply","type":"uint256"},{"internalType":"uint256","name":"totalMintedCount","type":"uint256"},{"internalType":"uint256","name":"lastSalePrice","type":"uint256"},{"internalType":"uint256","name":"currentPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getExhibitionIdForCollection","outputs":[{"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"getFeesAndRecipients","outputs":[{"internalType":"uint256","name":"totalFees","type":"uint256"},{"internalType":"uint256","name":"creatorRev","type":"uint256"},{"internalType":"address payable[]","name":"creatorRecipients","type":"address[]"},{"internalType":"uint256[]","name":"creatorShares","type":"uint256[]"},{"internalType":"uint256","name":"sellerRev","type":"uint256"},{"internalType":"address payable","name":"seller","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFethAddress","outputs":[{"internalType":"address","name":"fethAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getFixedPriceSale","outputs":[{"internalType":"address payable","name":"seller","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limitPerAccount","type":"uint256"},{"internalType":"uint256","name":"numberOfTokensAvailableToMint","type":"uint256"},{"internalType":"bool","name":"marketCanMint","type":"bool"},{"internalType":"uint256","name":"generalAvailabilityStartTime","type":"uint256"},{"internalType":"uint256","name":"earlyAccessStartTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"getFixedPriceSaleEarlyAccessAllowlistSupported","outputs":[{"internalType":"bool","name":"supported","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFoundationTreasury","outputs":[{"internalType":"address payable","name":"treasuryAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNftMarket","outputs":[{"internalType":"address","name":"nftMarket","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"time","type":"uint256"}],"name":"getPriceAtTimeForDutchAuction","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyRegistry","outputs":[{"internalType":"address","name":"registry","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"getSellerInfoFromDutchAuction","outputs":[{"internalType":"address","name":"seller","type":"address"},{"internalType":"bool","name":"creatorRevenueReadyForWithdrawal","type":"bool"},{"internalType":"bool","name":"creatorRevenueHasBeenWithdrawn","type":"bool"},{"internalType":"uint256","name":"totalFundsPendingDistribution","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getSellerOf","outputs":[{"internalType":"address payable","name":"seller","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"internalGetImmutableRoyalties","outputs":[{"internalType":"address payable[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"splitPerRecipientInBasisPoints","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address payable","name":"creator","type":"address"}],"name":"internalGetMutableRoyalties","outputs":[{"internalType":"address payable[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"splitPerRecipientInBasisPoints","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"internalGetTokenCreator","outputs":[{"internalType":"address payable","name":"creator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"mintFromDutchAuction","outputs":[{"internalType":"uint256","name":"firstTokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint16","name":"count","type":"uint16"},{"internalType":"address payable","name":"buyReferrer","type":"address"}],"name":"mintFromFixedPriceSale","outputs":[{"internalType":"uint256","name":"firstTokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"count","type":"uint256"},{"internalType":"address payable","name":"buyReferrer","type":"address"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"mintFromFixedPriceSaleWithEarlyAccessAllowlist","outputs":[{"internalType":"uint256","name":"firstTokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"address payable","name":"buyer","type":"address"}],"name":"rebateBuyerFromDutchAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"withdrawCreatorRevenueFromDutchAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6101806040523480156200001257600080fd5b50604051620062a4380380620062a48339810160408190526200003591620002c9565b816105dc84600187858a6001600160a01b0381163b620000685760405163028bba2560e61b815260040160405180910390fd5b6001600160a01b0390811660805281163b6200009757604051633d7a0d8f60e11b815260040160405180910390fd5b6001600160a01b0390811660a05281163b620000c65760405163de58082760e01b815260040160405180910390fd5b6001600160a01b031660c052620000e160646127106200035f565b620000ef906127106200035f565b8361ffff1610806200013d57506200010c61138861271062000382565b6200011c6103e86127106200035f565b6200012a906127106200035f565b6200013a9061ffff86166200039e565b10155b156200015c57604051630567777b60e41b815260040160405180910390fd5b61ffff831660e0526001600160a01b0382163b6200018d5760405163dd78160760e01b815260040160405180910390fd5b6001600160a01b0391821661010052151561014052306101205282163b15159050620001cc57604051630e4e467360e21b815260040160405180910390fd5b6001600160a01b031661016052620001e3620001ee565b5050505050620003b4565b600054610100900460ff16156200025b5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161015620002ae576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b0381168114620002c657600080fd5b50565b600080600080600060a08688031215620002e257600080fd5b8551620002ef81620002b0565b60208701519095506200030281620002b0565b60408701519094506200031581620002b0565b60608701519093506200032881620002b0565b60808701519092506200033b81620002b0565b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b6000826200037d57634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111562000398576200039862000349565b92915050565b8082018082111562000398576200039862000349565b60805160a05160c05160e05161010051610120516101405161016051615e366200046e600039600081816105c001528181613b6b01528181613c5d0152614d4b01526000818161414c0152614175015260008181613f600152818161400a01526140cd01526000818161063a0152612410015260006152570152600081816101f7015281816104a1015281816137bb01526149270152600081816104390152614b730152600081816107b80152614ea90152615e366000f3fe6080604052600436106101e75760003560e01c8063af1e1de311610102578063e965a20211610095578063f59488d911610064578063f59488d914610769578063f5ec797e14610789578063f7a2da23146107a9578063fb70943f146107dc57600080fd5b8063e965a202146106bf578063ecbc9554146106d2578063efef76f8146106e5578063f169dda31461070557600080fd5b8063d5391393116100d1578063d5391393146105e4578063d782d49114610618578063daa351d41461062b578063dccdafa51461065e57600080fd5b8063af1e1de314610505578063af4f5ac214610537578063bfb92b4214610557578063cafc21b3146105b157600080fd5b80634dad54a11161017a5780638129fc1c116101495780638129fc1c1461047d578063895633ba146104925780639901261c146104c55780639d460fc6146104e557600080fd5b80634dad54a1146103ea5780634fca06c61461040a5780636a90a8271461042a5780637f9d22fe1461045d57600080fd5b80632af2064f116101b65780632af2064f1461032d578063387fd4af1461034d57806342017634146103925780634c542f77146103b257600080fd5b806306ca634b146102555780630d7daf3e146102aa5780631722c7e7146102d85780632657c5581461030d57600080fd5b3661025057336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461024e576040517faa39384e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561026157600080fd5b506102756102703660046152bf565b6107fc565b604080516001600160a01b03909516855292151560208501529015159183019190915260608201526080015b60405180910390f35b3480156102b657600080fd5b506102ca6102c53660046152dc565b6108ff565b6040516102a192919061537c565b3480156102e457600080fd5b506102f86102f33660046153a1565b610b2a565b604080519283526020830191909152016102a1565b34801561031957600080fd5b5061024e6103283660046153ea565b610c34565b34801561033957600080fd5b5061024e6103483660046153a1565b610c6c565b34801561035957600080fd5b506103846103683660046152bf565b6001600160a01b031660009081526138db602052604090205490565b6040519081526020016102a1565b34801561039e57600080fd5b5061024e6103ad366004615444565b610dbb565b3480156103be57600080fd5b506103d26103cd3660046152dc565b610e81565b6040516001600160a01b0390911681526020016102a1565b3480156103f657600080fd5b506103846104053660046152dc565b610f12565b34801561041657600080fd5b506103d26104253660046152dc565b610ff6565b34801561043657600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103d2565b34801561046957600080fd5b5061024e610478366004615490565b611002565b34801561048957600080fd5b5061024e6117a1565b34801561049e57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103d2565b3480156104d157600080fd5b5061024e6104e0366004615527565b611933565b3480156104f157600080fd5b5061024e6105003660046155c5565b611ad2565b34801561051157600080fd5b50610525610520366004615609565b611ae8565b6040516102a19695949392919061563e565b34801561054357600080fd5b506103846105523660046153a1565b611b5b565b34801561056357600080fd5b506105a16105723660046152dc565b6001600160a01b039091166000908152613cc36020908152604080832093835260039093019052205460ff1690565b60405190151581526020016102a1565b3480156105bd57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103d2565b3480156105f057600080fd5b506103847f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610384610626366004615692565b611be0565b34801561063757600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103d2565b34801561066a57600080fd5b5061067e6106793660046152bf565b611d3f565b604080516001600160a01b03909816885260208801969096529486019390935260608501919091521515608084015260a083015260c082015260e0016102a1565b6103846106cd3660046152dc565b611ef2565b6103846106e0366004615734565b61234f565b3480156106f157600080fd5b506102ca610700366004615774565b6123d3565b34801561071157600080fd5b506107256107203660046152bf565b61299b565b60408051998a5260208a0198909852968801959095526060870193909352608086019190915260a085015260c084015260e0830152610100820152610120016102a1565b34801561077557600080fd5b5061024e6107843660046157ab565b612a6f565b34801561079557600080fd5b5061024e6107a436600461583a565b612a8d565b3480156107b557600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103d2565b3480156107e857600080fd5b5061024e6107f73660046152bf565b612dea565b6001600160a01b03811660009081526140ac602052604081208054829182918291907c0100000000000000000000000000000000000000000000000000000000900463ffffffff16156108f757600281015460018201546001600160a01b039091169550760100000000000000000000000000000000000000000000900460ff169250826108f757600181015463ffffffff66010000000000008204811662010000909204161015806108d5575080547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1642115b600182015490945062010000900463ffffffff166108f38742610f12565b0291505b509193509193565b6060806109356001600160a01b0385167f2a55205a00000000000000000000000000000000000000000000000000000000613037565b15610a3f576040517f2a55205a0000000000000000000000000000000000000000000000000000000081526004810184905261271060248201526001600160a01b03851690632a55205a90619c409060440160408051808303818786fa935050505080156109c0575060408051601f3d908101601f191682019092526109bd91810190615896565b60015b15610a3f578015610a3c57604080516001808252818301909252906020808301908036833701905050935081846000815181106109ff576109ff6158f3565b6001600160a01b03929092166020928302919091018201526040805160018082528183019092529182810190803683370190505092505050610b23565b50505b610a726001600160a01b0385167fbb3bafd600000000000000000000000000000000000000000000000000000000613037565b15610b23576040517fbb3bafd6000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0385169063bb3bafd690619c40906024016000604051808303818786fa93505050508015610afb57506040513d6000823e601f3d908101601f19168201604052610af89190810190615a46565b60015b15610b2357815115801590610b11575080518251145b15610b20579092509050610b23565b50505b9250929050565b6001600160a01b0382811660009081526140ac6020908152604080832093851683526003840182528083208151808301909252546bffffffffffffffffffffffff811682526c01000000000000000000000000900461ffff169181018290529192839290918390610b9b8842610f12565b835191029150610bba9082906bffffffffffffffffffffffff16615ad9565b6001840154602084015191965061ffff9081169116811115610c29576020830151600185015461ffff9091168203955063ffffffff660100000000000082048116916201000090041680821115610c2157808203915081871115610c1c578196505b610c26565b600096505b50505b505050509250929050565b610c678360008469ffffffffffffffffffff168461ffff1642426000801b60405180602001604052806000815250613102565b505050565b6001600160a01b0380831660009081526140ac602090815260408083209385168352600390930190529081208054909161ffff6c01000000000000000000000000830416916bffffffffffffffffffffffff1690610cca8642610f12565b90508281026000610cdb8285615ad9565b905080600003610d17576040517f433b2e9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff8316178655610d5b878262033450613718565b866001600160a01b0316886001600160a01b03167fd9f7a958c85633cf171f617fa7127c9f19bacb103e2bce41bb6728d0476515808386604051610da9929190918252602082015260400190565b60405180910390a35050505050505050565b808015801590610dcf5750610dcf81421190565b15610e06576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82600003610e1657429250610e56565b610e1f83421190565b15610e56576040517fa619834a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e788787878787886000801b60405180602001604052806000815250613102565b50505050505050565b6040517f40c1a064000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b038416906340c1a06490619c40906024016020604051808303818786fa158015610ee6573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610f0b9190615aec565b9392505050565b6001600160a01b03821660009081526140ac60205260408120600181015463ffffffff660100000000000082048116620100009092041610610f765760018101546a010000000000000000000090046bffffffffffffffffffffffff169150610fef565b8054610fec906bffffffffffffffffffffffff808216916c0100000000000000000000000081049091169063ffffffff780100000000000000000000000000000000000000000000000082048116917c010000000000000000000000000000000000000000000000000000000090041687613863565b91505b5092915050565b6000610f0b83836138ba565b866110366001600160a01b0382167f5bf6f7b800000000000000000000000000000000000000000000000000000000613961565b61106c576040517f17fdef0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61109f6001600160a01b0382167f80ac58cd00000000000000000000000000000000000000000000000000000000613037565b6110d5576040517f29d190d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a660048201523060248201526001600160a01b038216906391d1485490604401602060405180830381865afa158015611158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117c9190615b09565b6111b2576040517f436d641100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b876001600160a01b0381166391d1485460006111cc61397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381865afa15801561122e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112529190615b09565b611288576040517f5fad784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b886112928161398c565b156112c9576040517f2540293000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b868811611302576040517f5a42b07900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8560000361133c576040517f92865bfe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460000361134c5742945061138c565b61135585421190565b1561138c576040517f3a3baeca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836000036113c6576040517f3335f9a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006113d28587615b2b565b90506303c267004201811115611421576040517f82fae596000000000000000000000000000000000000000000000000000000008152426303c267000160048201526024015b60405180910390fd5b60008b6001600160a01b0316638ae3e5f16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611461573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114859190615b3e565b9050806000036114c1576040517f61b9c86b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006114cb61397d565b6001600160a01b038e1660009081526140ac602052604090209091506114f08c613997565b815477ffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff878116919091027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff16919091177801000000000000000000000000000000000000000000000000918c1691909102177fffffffffffffffff000000000000000000000000000000000000000000000000166c010000000000000000000000006bffffffffffffffffffffffff8e8116919091027fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169190911791161781556115f68a613a39565b6115ff84613acd565b6002830180546001600160a01b0386167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617905560018301805461ffff9093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000063ffffffff909316660100000000000002929092167fffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff0000909316929092171790556116ad8e8e613b63565b6040517f24ef95e7000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b038f16906324ef95e790602401600060405180830381600087803b15801561170857600080fd5b505af115801561171c573d6000803e3d6000fd5b50505050816001600160a01b03168e6001600160a01b03167f4be408b58dfcdd9eed5e094c36b581760de5ad0b07dc4fc948b61fba5001898e8e8e8e8e8a604051611789959493929190948552602085019390935260408401919091526060830152608082015260a00190565b60405180910390a35050505050505050505050505050565b600054610100900460ff16158080156117c15750600054600160ff909116105b806117db5750303b1580156117db575060005460ff166001145b611867576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401611418565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156118c557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6118cd613d8d565b801561193057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b808015801590611947575061194781421190565b1561197e576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848484826119b8576040517f229955da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008190036119f3576040517fe85bdf0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b88600003611a0357429850611a43565b611a0c89421190565b15611a43576040517fdddbb7cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b898910611a7c576040517f77b2af4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ac28e8e8e8e8e8e8e8e8e8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061310292505050565b5050505050505050505050505050565b611ae185600086868686610dbb565b5050505050565b600080606080600080611afb8989613e2e565b9050611b0c8989838a600080613ef1565b5093995091965094509250600090505b8351811015611b4e57838181518110611b3757611b376158f3565b602002602001015186019550806001019050611b1c565b5093975093979195509350565b600080600080611b6a86611d3f565b5050945094509450505080611b855760009350505050611bda565b6001600160a01b038087166000908152613cc3602090815260408083209389168352600190930190522054838110611bc4576000945050505050611bda565b808403945082851115611bd5578294505b505050505b92915050565b6001600160a01b0385166000908152613cc360205260408120600281015463ffffffff16421015611d28576002810154640100000000900463ffffffff16421015611cba57600281015463ffffffff8082166401000000009092041603611c73576040517f77b2af4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028101546040517f8ba20e6800000000000000000000000000000000000000000000000000000000815264010000000090910463ffffffff166004820152602401611418565b6000611cd98585611cc961397d565b6001600160a01b0316919061459d565b600081815260038401602052604090205490915060ff16611d26576040517ff88937c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b611d34818888886145f9565b979650505050505050565b6000806000806000806000876001600160a01b0316638ae3e5f16040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611da4575060408051601f3d908101601f19168201909252611da191810190615b3e565b60015b15611ee7578015611ee5576040517f91d148540000000000000000000000000000000000000000000000000000000081527f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a660048201523060248201526001600160a01b038a16906391d1485490604401602060405180830381865afa925050508015611e4e575060408051601f3d908101601f19168201909252611e4b91810190615b09565b60015b15611ee5576001600160a01b038a81166000908152613cc36020526040902080546002909101549181169a5074010000000000000000000000000000000000000000810469ffffffffffffffffffff1699507e01000000000000000000000000000000000000000000000000000000000000900461ffff169750919550935063ffffffff8082169350640100000000909104169050835b505b919395979092949650565b600081600003611f2e576040517fb3896a5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660009081526140ac60205260408120805490917c010000000000000000000000000000000000000000000000000000000090910463ffffffff169003611faa576040517fcc4f974c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547801000000000000000000000000000000000000000000000000900463ffffffff1642101561202d5780546040517f70fdd722000000000000000000000000000000000000000000000000000000008152780100000000000000000000000000000000000000000000000090910463ffffffff166004820152602401611418565b600061203761397d565b6001600160a01b03811660009081526003840160205260408120805492935091612075906c01000000000000000000000000900461ffff1687615b2b565b600185015490915061ffff168111156120df57815460018501546040517fb3c84a0b0000000000000000000000000000000000000000000000000000000081526c0100000000000000000000000090920461ffff9081166004840152166024820152604401611418565b60006120eb8842610f12565b600186015490915087820290760100000000000000000000000000000000000000000000810460ff169061212b9062010000900463ffffffff168a615b2b565b60018801805463ffffffff90921662010000027fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff6bffffffffffffffffffffffff8088166a010000000000000000000002919091167fffffffffffffffffffff000000000000000000000000ffffffff00000000ffff9094169390931717905585546121c1916121bc911684615b2b565b613997565b85547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166c0100000000000000000000000061ffff8716027fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016176bffffffffffffffffffffffff9190911617855561223b82600161491d565b6040517fd115124900000000000000000000000000000000000000000000000000000000815261ffff8a1660048201526001600160a01b0387811660248301528b169063d1151249906044016020604051808303816000875af11580156122a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ca9190615b3e565b975080156122f15760028701546122ee908b9084906001600160a01b03168b614a07565b50505b60408051848152602081018b90529081018990526001600160a01b0380881691908c16907fd63692b8fbf23357add4e0ac130de567b601498f82505d23b329b450c74945059060600160405180910390a35050505050505092915050565b6001600160a01b0383166000908152613cc360205260408120600281015463ffffffff164210156123ba5760028101546040517f26763e5a00000000000000000000000000000000000000000000000000000000815263ffffffff9091166004820152602401611418565b6123ca81868661ffff16866145f9565b95945050505050565b6040517fde5488af0000000000000000000000000000000000000000000000000000000081526001600160a01b03848116600483015260609182917f0000000000000000000000000000000000000000000000000000000000000000169063de5488af90619c40906024016020604051808303818786fa93505050508015612478575060408051601f3d908101601f1916820190925261247591810190615aec565b60015b156126c957856001600160a01b0316816001600160a01b0316146126c7579450846124cc6001600160a01b0382167f2a55205a00000000000000000000000000000000000000000000000000000000613037565b156125d7576040517f2a55205a0000000000000000000000000000000000000000000000000000000081526004810186905261271060248201526001600160a01b03871690632a55205a90619c409060440160408051808303818786fa93505050508015612557575060408051601f3d908101601f1916820190925261255491810190615896565b60015b156125d75780156125d45760408051600180825281830190925290602080830190803683370190505094508185600081518110612596576125966158f3565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337019050509350505050612993565b50505b825115801561261457506126146001600160a01b0387167fbb3bafd600000000000000000000000000000000000000000000000000000000613037565b156126c7576040517fbb3bafd6000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b0387169063bb3bafd690619c40906024016000604051808303818786fa9350505050801561269d57506040513d6000823e601f3d908101601f1916820160405261269a9190810190615a46565b60015b156126c7578151158015906126b3575080518251145b156126c45790935091506129939050565b50505b505b6126fc6001600160a01b0386167fb779958400000000000000000000000000000000000000000000000000000000613037565b15612831576040517fb9c4d9fb000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b0386169063b9c4d9fb90619c40906024016000604051808303818786fa9350505050801561278557506040513d6000823e601f3d908101601f191682016040526127829190810190615b57565b60015b156128315780511561282f576040517f0ebd4c7f000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03871690630ebd4c7f90619c40906024016000604051808303818786fa9350505050801561281557506040513d6000823e601f3d908101601f191682016040526128129190810190615b8c565b60015b1561282f57805182510361282d579092509050612993565b505b505b6001600160a01b038316156128af5760408051600180825281830190925290602080830190803683370190505091508282600081518110612874576128746158f3565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337019050509050612993565b846001600160a01b0316638da5cb5b619c406040518263ffffffff1660e01b81526004016020604051808303818786fa9350505050801561290d575060408051601f3d908101601f1916820190925261290a91810190615aec565b60015b15612993576001600160a01b038116156129915760408051600180825281830190925290602080830190803683370190505092508083600081518110612955576129556158f3565b6001600160a01b039290921660209283029190910182015260408051600180825281830190925291828101908036833701905050915050612993565b505b935093915050565b6001600160a01b03811660009081526140ac60205260408120805460018201546bffffffffffffffffffffffff808316946c01000000000000000000000000840482169461ffff84169463ffffffff780100000000000000000000000000000000000000000000000082048116957c010000000000000000000000000000000000000000000000000000000090920481169466010000000000008304821694620100008404909216936a01000000000000000000009093041691612a5f8b42610f12565b9150509193959799909294969850565b612a828960008a8a8a8a8a8a8a8a611933565b505050505050505050565b83806001600160a01b0316638ae3e5f16040518163ffffffff1660e01b8152600401602060405180830381865afa158015612acc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612af09190615b3e565b600003612b29576040517f3c263ade00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846001600160a01b0381166391d148546000612b4361397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381865afa158015612ba5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bc99190615b09565b612bff576040517f5fad784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84848482612c39576040517f229955da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000819003612c74576040517fe85bdf0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0389166000908152613cc360205260409020600281015463ffffffff164210612d115780546001600160a01b0316612cdf576040517f974bcaec00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517ff2279aef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281015463ffffffff64010000000082048116911603612d5e576040517f77b2af4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008981526003820160205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055516001600160a01b038b16907f7f11be7894109a714225fbb6c33d88a14582407d653ac3fc5abf07d9b3ce891490612dd6908c908c908c90615bc1565b60405180910390a250505050505050505050565b6001600160a01b0380821660009081526140ac60205260409020600281015490911680612e43576040517fcc4f974c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001820154760100000000000000000000000000000000000000000000900460ff1615612e9c576040517fba5a58b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600182015482546201000090910463ffffffff908116917c01000000000000000000000000000000000000000000000000000000009004164211158015612ef6575060018301546601000000000000900463ffffffff1681105b15612f7257825460018401546040517f1dd4212a0000000000000000000000000000000000000000000000000000000081527c010000000000000000000000000000000000000000000000000000000090920463ffffffff90811660048401526601000000000000909104168290036024820152604401611418565b6000612f7e8542610f12565b6001850180547fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000001790559050818102600080612fd588848884614a07565b60408051878152602081018990529081018390526060810182905291935091506001600160a01b038916907f5e16e96b4ba4fe46f3be73d54d1fa0da481494ab74c2d6e33328366d6437693c9060800160405180910390a25050505050505050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d915060005190508280156130ef575060208210155b8015611d34575015159695505050505050565b876131366001600160a01b0382167f5bf6f7b800000000000000000000000000000000000000000000000000000000613961565b61316c576040517f17fdef0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61319f6001600160a01b0382167f80ac58cd00000000000000000000000000000000000000000000000000000000613037565b6131d5576040517f29d190d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a660048201523060248201526001600160a01b038216906391d1485490604401602060405180830381865afa158015613258573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327c9190615b09565b6132b2576040517f436d641100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b88806001600160a01b0316638ae3e5f16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133159190615b3e565b60000361334e576040517f3c263ade00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b896001600160a01b0381166391d14854600061336861397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381865afa1580156133ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ee9190615b09565b613424576040517f5fad784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b866303c26700420181111561346d576040517fe43fc8b2000000000000000000000000000000000000000000000000000000008152426303c26700016004820152602401611418565b8b6134778161398c565b156134ae576040517f2540293000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b896000036134e8576040517f250099ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038d166000908152613cc3602052604081209061350a61397d565b82547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03821617835590506135468d614a3a565b825469ffffffffffffffffffff9190911674010000000000000000000000000000000000000000027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff90911617825561359e8c613a39565b825461ffff919091167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116178255428b14613627576002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff8d161790555b428a14613668576002820180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1664010000000063ffffffff8d16021790555b88156136a8576000898152600383016020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555b6136b28f8f613b63565b806001600160a01b03168f6001600160a01b03167fa4e684574cd21d7eb4df36ec6d7f86d16aa900ceddae41e4e82f8f4170f293b98f8f8f8f8f8f6040516136ff96959493929190615c1b565b60405180910390a3505050505050505050505050505050565b8160000361372557505050565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d8060008114613775576040519150601f19603f3d011682016040523d82523d6000602084013e61377a565b606091505b505090508061385d576040517faa67c9190000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063aa67c9199085906024016000604051808303818588803b15801561380057600080fd5b505af1158015613814573d6000803e3d6000fd5b5050505050836001600160a01b03167fa2201512569adb2d513531dfd69b66df50bd5cffb8c1bbe65a4611f9e1eadbd18460405161385491815260200190565b60405180910390a25b50505050565b60008382116138735750846123ca565b8282106138815750836123ca565b61388b8587615ad9565b905081830361389a8183615c71565b915084840382816138ad576138ad615c88565b049150611d348683615b2b565b6040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa925050508015613937575060408051601f3d908101601f1916820190925261393491810190615aec565b60015b15613957576001600160a01b03811615613955576000915050611bda565b505b610f0b8383614ad6565b600061396c83614b04565b8015610f0b5750610f0b8383613037565b6000613987614b68565b905090565b6000611bda82614bc5565b60006bffffffffffffffffffffffff821115613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401611418565b5090565b600061ffff821115613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401611418565b600063ffffffff821115613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f32206269747300000000000000000000000000000000000000000000000000006064820152608401611418565b8015613d89577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e06db9682613ba161397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381865afa158015613c03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c279190615b09565b613d44576040517f55daed3e000000000000000000000000000000000000000000000000000000008152600481018290526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906355daed3e906024016040805180830381865afa158015613cab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ccf9190615cb7565b5090506001600160a01b038116613d12576040517f167ce11a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa0cfce1100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660008181526138db6020526040808220849055518392917fa69ac11ca23c60fc41e8e7b0217b2da8c6290a60e39202b1a24b126f47480b4791a35b5050565b600054610100900460ff16613e24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401611418565b613e2c614bf7565b565b6040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa925050508015613eab575060408051601f3d908101601f19168201909252613ea891810190615aec565b60015b15613957576001600160a01b03811615613955576040517f5ca60e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006060806000806000612710613f078d614c96565b613f11908b615c71565b613f1b9190615ce6565b6040517f4c542f770000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e90529197506000917f00000000000000000000000000000000000000000000000000000000000000001690634c542f7790604401602060405180830381865afa925050508015613fc3575060408051601f3d908101601f19168201909252613fc091810190615aec565b60015b15613fcb5790505b6040517f0d7daf3e0000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e90527f00000000000000000000000000000000000000000000000000000000000000001690630d7daf3e90604401600060405180830381865afa92505050801561407257506040513d6000823e601f3d908101601f1916820160405261406f9190810190615a46565b60015b1561407d5790965094505b8551600003614140576040517fefef76f80000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e905282811660448301527f0000000000000000000000000000000000000000000000000000000000000000169063efef76f890606401600060405180830381865afa92505050801561413557506040513d6000823e601f3d908101601f191682016040526141329190810190615a46565b60015b156141405790965094505b855115158061416c57507f00000000000000000000000000000000000000000000000000000000000000005b156144d65760007f00000000000000000000000000000000000000000000000000000000000000001561421757878b03905086516000036142125760408051600180825281830190925290602080830190803683370190505096508b876000815181106141db576141db6158f3565b6001600160a01b03929092166020928302919091018201526040805160018082528183019092529182810190803683370190505095505b614298565b816001600160a01b03168c6001600160a01b0316148061426d575086511580159061426d575086600081518110614250576142506158f3565b60200260200101516001600160a01b03168c6001600160a01b0316145b1561427b5750868a03614298565b50600a8a048061428b898d615ad9565b6142959190615ad9565b94505b6142a3876005614ca1565b6142ae866005614ca1565b61ffff8916156142fb576127106142c961ffff8b168d615c71565b6142d39190615ce6565b9250846000036142ee576142e78382615ad9565b90506142fb565b6142f88386615ad9565b94505b600060018851111561442f5760005b88518110156143e7578d6001600160a01b031689828151811061432f5761432f6158f3565b60200260200101516001600160a01b03160361434e5760009692909201915b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82146143df5761271088828151811061438a5761438a6158f3565b602002602001015111156143c0577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91506143df565b8781815181106143d2576143d26158f3565b6020026020010151820191505b60010161430a565b5080158061441457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81145b1561442f57614424886001614ca1565b61442f876001614ca1565b600060015b89518110156144a4576000838a8381518110614452576144526158f3565b6020026020010151866144659190615c71565b61446f9190615ce6565b905061447b8184615b2b565b9250808a8381518110614490576144906158f3565b602090810291909101015250600101614434565b506144af8184615ad9565b886000815181106144c2576144c26158f3565b60200260200101818152505050505061450f565b868a03935061ffff88161561450f576127106144f661ffff8a168c615c71565b6145009190615ce6565b915061450c8285615ad9565b93505b6001600160a01b03891615801590614540575061452a61397d565b6001600160a01b0316896001600160a01b031614155b801561455e57508a6001600160a01b0316896001600160a01b031614155b801561457c5750806001600160a01b0316896001600160a01b031614155b1561458d5760648a04925082870396505b5096509650965096509650969050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085901b166020820152600090819060340160408051601f19818403018152919052805160209091012090506123ca848483614cae565b600082600003614635576040517f2d69aaf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061463f61397d565b6001600160a01b038116600090815260018801602052604081205491925090614669908690615b2b565b87549091507e01000000000000000000000000000000000000000000000000000000000000900461ffff168111156147585786547e01000000000000000000000000000000000000000000000000000000000000900461ffff166000036146fc576040517f974bcaec00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86546040517ffa7c028e0000000000000000000000000000000000000000000000000000000081527e0100000000000000000000000000000000000000000000000000000000000090910461ffff166004820152602401611418565b6001600160a01b03821660009081526001880160205260409020819055865474010000000000000000000000000000000000000000900469ffffffffffffffffffff168502348110156147da576040517f9239ca9d00000000000000000000000000000000000000000000000000000000815260048101829052602401611418565b6147e581600061491d565b6040517fd115124900000000000000000000000000000000000000000000000000000000815261ffff871660048201526001600160a01b03848116602483015288169063d1151249906044016020604051808303816000875af1158015614850573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148749190615b3e565b935060008061488289614cfa565b8b54919350915060009081906148a8908c908a906001600160a01b0316888d8989614dc9565b509150915087876001600160a01b03168c6001600160a01b03167f05ebbb6b0ce7d564230ba625dd7a0e5108786b0852d6060de6099e1778203e348d8686604051614906939291909283526020830191909152604082015260600190565b60405180910390a450505050505050949350505050565b348211156149d9577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663452f2b8f61495c61397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b0390911660048201523485036024820152604401600060405180830381600087803b1580156149bd57600080fd5b505af11580156149d1573d6000803e3d6000fd5b505050505050565b8080156149e557503482105b15613d8957613d898234036149f861397d565b6001600160a01b031690614ff0565b600080600080614a1688614cfa565b91509150614a2a8886888a60008787614dc9565b5090999098509650505050505050565b600069ffffffffffffffffffff821115613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201527f30206269747300000000000000000000000000000000000000000000000000006064820152608401611418565b6001600160a01b0380831660009081526140ac60205260409020600201541680611bda57610f0b838361513d565b6000614b30827f01ffc9a700000000000000000000000000000000000000000000000000000000613037565b8015611bda5750614b61827fffffffff00000000000000000000000000000000000000000000000000000000613037565b1592915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168103614bc257507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec36013560601c5b90565b6001600160a01b0381811660009081526140ac6020526040812060020154909116151580611bda5750611bda82615168565b600054610100900460ff16614c8e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401611418565b6001610dad55565b6000611bda82615195565b8082511115613d89579052565b600081815b84811015614cf157614cdd82878784818110614cd157614cd16158f3565b9050602002013561527b565b915080614ce981615d21565b915050614cb3565b50949350505050565b6001600160a01b03811660009081526138db602052604081205481908015614dc3576040517f55daed3e000000000000000000000000000000000000000000000000000000008152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906355daed3e906024016040805180830381865afa158015614d99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614dbd9190615cb7565b90935091505b50915091565b600080600086600003614de457506000915081905080614fe3565b606080600080614df88e8e8e8e8e8d613ef1565b8451959c509199509297509095509093509150600090600114614e1d57614e20614e22565b620334505b905060005b8551811015614e9657614e6d868281518110614e4557614e456158f3565b6020026020010151868381518110614e5f57614e5f6158f3565b602002602001015184613718565b848181518110614e7f57614e7f6158f3565b602002602001015188019750806001019050614e27565b50614ea48d87614e20613718565b614ed17f000000000000000000000000000000000000000000000000000000000000000089614e20613718565b8215614f4c57614ee48b84614e20613718565b8d8f6001600160a01b03167f141b92fd9766c80ab120598ea2f6be9802470ec59b5446dd9bf46214ead8d08e8d866000604051614f3f939291906001600160a01b039390931683526020830191909152604082015260600190565b60405180910390a3968201965b6001600160a01b038a1615614fdd578115614f845785600003614f725795810195614f77565b948101945b614f848a83614e20613718565b8d8f6001600160a01b03167f27a4dd4ff659a9e6354fb079b2208365e5b83f55c22a4150eee2bca89501cb988c85604051614fd49291906001600160a01b03929092168252602082015260400190565b60405180910390a35b50505050505b9750975097945050505050565b8047101561505a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401611418565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146150a7576040519150601f19603f3d011682016040523d82523d6000602084013e6150ac565b606091505b5050905080610c67576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401611418565b600061514883611d3f565b50949550506001600160a01b0385169350611bda92505050576000610f0b565b6001600160a01b038181166000908152613cc36020526040812054909116151580611bda57506000611bda565b6000816001600160a01b031663dfea951d6040518163ffffffff1660e01b8152600401600060405180830381865afa9250505080156151f657506040513d6000823e601f3d908101601f191682016040526151f39190810190615d59565b60015b15615255576040517fee2afa3f960e108aca17013728aafa363a0f4485661d9b6f41c6b4ddb55008ee9061522e908390602001615de4565b604051602081830303815290604052805190602001200361525357506101f492915050565b505b7f0000000000000000000000000000000000000000000000000000000000000000611bda565b6000818310615297576000828152602084905260409020610f0b565b6000838152602083905260409020610f0b565b6001600160a01b038116811461193057600080fd5b6000602082840312156152d157600080fd5b8135610f0b816152aa565b600080604083850312156152ef57600080fd5b82356152fa816152aa565b946020939093013593505050565b600081518084526020808501945080840160005b838110156153415781516001600160a01b03168752958201959082019060010161531c565b509495945050505050565b600081518084526020808501945080840160005b8381101561534157815187529582019590820190600101615360565b60408152600061538f6040830185615308565b82810360208401526123ca818561534c565b600080604083850312156153b457600080fd5b82356153bf816152aa565b915060208301356153cf816152aa565b809150509250929050565b61ffff8116811461193057600080fd5b6000806000606084860312156153ff57600080fd5b833561540a816152aa565b9250602084013569ffffffffffffffffffff8116811461542957600080fd5b91506040840135615439816153da565b809150509250925092565b60008060008060008060c0878903121561545d57600080fd5b8635615468816152aa565b9860208801359850604088013597606081013597506080810135965060a00135945092505050565b600080600080600080600060e0888a0312156154ab57600080fd5b87356154b6816152aa565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b60008083601f8401126154f757600080fd5b50813567ffffffffffffffff81111561550f57600080fd5b602083019150836020828501011115610b2357600080fd5b6000806000806000806000806000806101208b8d03121561554757600080fd5b8a35615552816152aa565b995060208b0135985060408b0135975060608b0135965060808b0135955060a08b0135945060c08b0135935060e08b013567ffffffffffffffff81111561559857600080fd5b6155a48d828e016154e5565b915080945050809250506101008b013590509295989b9194979a5092959850565b600080600080600060a086880312156155dd57600080fd5b85356155e8816152aa565b97602087013597506040870135966060810135965060800135945092505050565b60008060006060848603121561561e57600080fd5b8335615629816152aa565b95602085013595506040909401359392505050565b86815285602082015260c06040820152600061565d60c0830187615308565b828103606084015261566f818761534c565b9150508360808301526001600160a01b03831660a0830152979650505050505050565b6000806000806000608086880312156156aa57600080fd5b85356156b5816152aa565b94506020860135935060408601356156cc816152aa565b9250606086013567ffffffffffffffff808211156156e957600080fd5b818801915088601f8301126156fd57600080fd5b81358181111561570c57600080fd5b8960208260051b850101111561572157600080fd5b9699959850939650602001949392505050565b60008060006060848603121561574957600080fd5b8335615754816152aa565b92506020840135615764816153da565b91506040840135615439816152aa565b60008060006060848603121561578957600080fd5b8335615794816152aa565b9250602084013591506040840135615439816152aa565b60008060008060008060008060006101008a8c0312156157ca57600080fd5b89356157d5816152aa565b985060208a0135975060408a0135965060608a0135955060808a0135945060a08a0135935060c08a013567ffffffffffffffff81111561581457600080fd5b6158208c828d016154e5565b9a9d999c50979a9699959894979660e00135949350505050565b6000806000806060858703121561585057600080fd5b843561585b816152aa565b935060208501359250604085013567ffffffffffffffff81111561587e57600080fd5b61588a878288016154e5565b95989497509550505050565b600080604083850312156158a957600080fd5b82516158b4816152aa565b6020939093015192949293505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561594b5761594b6158c4565b604052919050565b600067ffffffffffffffff82111561596d5761596d6158c4565b5060051b60200190565b600082601f83011261598857600080fd5b8151602061599d61599883615953565b615922565b82815260059290921b840181019181810190868411156159bc57600080fd5b8286015b848110156159e05780516159d3816152aa565b83529183019183016159c0565b509695505050505050565b600082601f8301126159fc57600080fd5b81516020615a0c61599883615953565b82815260059290921b84018101918181019086841115615a2b57600080fd5b8286015b848110156159e05780518352918301918301615a2f565b60008060408385031215615a5957600080fd5b825167ffffffffffffffff80821115615a7157600080fd5b615a7d86838701615977565b93506020850151915080821115615a9357600080fd5b50615aa0858286016159eb565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115611bda57611bda615aaa565b600060208284031215615afe57600080fd5b8151610f0b816152aa565b600060208284031215615b1b57600080fd5b81518015158114610f0b57600080fd5b80820180821115611bda57611bda615aaa565b600060208284031215615b5057600080fd5b5051919050565b600060208284031215615b6957600080fd5b815167ffffffffffffffff811115615b8057600080fd5b610fec84828501615977565b600060208284031215615b9e57600080fd5b815167ffffffffffffffff811115615bb557600080fd5b610fec848285016159eb565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b60005b83811015615c12578181015183820152602001615bfa565b50506000910152565b86815285602082015284604082015283606082015282608082015260c060a0820152600082518060c0840152615c588160e0850160208701615bf7565b601f01601f19169190910160e001979650505050505050565b8082028115828204841417611bda57611bda615aaa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008060408385031215615cca57600080fd5b8251615cd5816152aa565b60208401519092506153cf816153da565b600082615d1c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615d5257615d52615aaa565b5060010190565b600060208284031215615d6b57600080fd5b815167ffffffffffffffff80821115615d8357600080fd5b818401915084601f830112615d9757600080fd5b815181811115615da957615da96158c4565b615dbc6020601f19601f84011601615922565b9150808252856020828501011115615dd357600080fd5b614cf1816020840160208601615bf7565b60008251615df6818460208701615bf7565b919091019291505056fea26469706673582212204ecb477e302353cd291bd1cf37495d404024a009e937ad1f4a9eb6c5b82f738264736f6c6343000813003300000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb600000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e3
Deployed Bytecode
0x6080604052600436106101e75760003560e01c8063af1e1de311610102578063e965a20211610095578063f59488d911610064578063f59488d914610769578063f5ec797e14610789578063f7a2da23146107a9578063fb70943f146107dc57600080fd5b8063e965a202146106bf578063ecbc9554146106d2578063efef76f8146106e5578063f169dda31461070557600080fd5b8063d5391393116100d1578063d5391393146105e4578063d782d49114610618578063daa351d41461062b578063dccdafa51461065e57600080fd5b8063af1e1de314610505578063af4f5ac214610537578063bfb92b4214610557578063cafc21b3146105b157600080fd5b80634dad54a11161017a5780638129fc1c116101495780638129fc1c1461047d578063895633ba146104925780639901261c146104c55780639d460fc6146104e557600080fd5b80634dad54a1146103ea5780634fca06c61461040a5780636a90a8271461042a5780637f9d22fe1461045d57600080fd5b80632af2064f116101b65780632af2064f1461032d578063387fd4af1461034d57806342017634146103925780634c542f77146103b257600080fd5b806306ca634b146102555780630d7daf3e146102aa5780631722c7e7146102d85780632657c5581461030d57600080fd5b3661025057336001600160a01b037f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443161461024e576040517faa39384e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561026157600080fd5b506102756102703660046152bf565b6107fc565b604080516001600160a01b03909516855292151560208501529015159183019190915260608201526080015b60405180910390f35b3480156102b657600080fd5b506102ca6102c53660046152dc565b6108ff565b6040516102a192919061537c565b3480156102e457600080fd5b506102f86102f33660046153a1565b610b2a565b604080519283526020830191909152016102a1565b34801561031957600080fd5b5061024e6103283660046153ea565b610c34565b34801561033957600080fd5b5061024e6103483660046153a1565b610c6c565b34801561035957600080fd5b506103846103683660046152bf565b6001600160a01b031660009081526138db602052604090205490565b6040519081526020016102a1565b34801561039e57600080fd5b5061024e6103ad366004615444565b610dbb565b3480156103be57600080fd5b506103d26103cd3660046152dc565b610e81565b6040516001600160a01b0390911681526020016102a1565b3480156103f657600080fd5b506103846104053660046152dc565b610f12565b34801561041657600080fd5b506103d26104253660046152dc565b610ff6565b34801561043657600080fd5b507f000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e36103d2565b34801561046957600080fd5b5061024e610478366004615490565b611002565b34801561048957600080fd5b5061024e6117a1565b34801561049e57600080fd5b507f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436103d2565b3480156104d157600080fd5b5061024e6104e0366004615527565b611933565b3480156104f157600080fd5b5061024e6105003660046155c5565b611ad2565b34801561051157600080fd5b50610525610520366004615609565b611ae8565b6040516102a19695949392919061563e565b34801561054357600080fd5b506103846105523660046153a1565b611b5b565b34801561056357600080fd5b506105a16105723660046152dc565b6001600160a01b039091166000908152613cc36020908152604080832093835260039093019052205460ff1690565b60405190151581526020016102a1565b3480156105bd57600080fd5b507f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f6103d2565b3480156105f057600080fd5b506103847f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610384610626366004615692565b611be0565b34801561063757600080fd5b507f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d6103d2565b34801561066a57600080fd5b5061067e6106793660046152bf565b611d3f565b604080516001600160a01b03909816885260208801969096529486019390935260608501919091521515608084015260a083015260c082015260e0016102a1565b6103846106cd3660046152dc565b611ef2565b6103846106e0366004615734565b61234f565b3480156106f157600080fd5b506102ca610700366004615774565b6123d3565b34801561071157600080fd5b506107256107203660046152bf565b61299b565b60408051998a5260208a0198909852968801959095526060870193909352608086019190915260a085015260c084015260e0830152610100820152610120016102a1565b34801561077557600080fd5b5061024e6107843660046157ab565b612a6f565b34801561079557600080fd5b5061024e6107a436600461583a565b612a8d565b3480156107b557600080fd5b507f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb66103d2565b3480156107e857600080fd5b5061024e6107f73660046152bf565b612dea565b6001600160a01b03811660009081526140ac602052604081208054829182918291907c0100000000000000000000000000000000000000000000000000000000900463ffffffff16156108f757600281015460018201546001600160a01b039091169550760100000000000000000000000000000000000000000000900460ff169250826108f757600181015463ffffffff66010000000000008204811662010000909204161015806108d5575080547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1642115b600182015490945062010000900463ffffffff166108f38742610f12565b0291505b509193509193565b6060806109356001600160a01b0385167f2a55205a00000000000000000000000000000000000000000000000000000000613037565b15610a3f576040517f2a55205a0000000000000000000000000000000000000000000000000000000081526004810184905261271060248201526001600160a01b03851690632a55205a90619c409060440160408051808303818786fa935050505080156109c0575060408051601f3d908101601f191682019092526109bd91810190615896565b60015b15610a3f578015610a3c57604080516001808252818301909252906020808301908036833701905050935081846000815181106109ff576109ff6158f3565b6001600160a01b03929092166020928302919091018201526040805160018082528183019092529182810190803683370190505092505050610b23565b50505b610a726001600160a01b0385167fbb3bafd600000000000000000000000000000000000000000000000000000000613037565b15610b23576040517fbb3bafd6000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0385169063bb3bafd690619c40906024016000604051808303818786fa93505050508015610afb57506040513d6000823e601f3d908101601f19168201604052610af89190810190615a46565b60015b15610b2357815115801590610b11575080518251145b15610b20579092509050610b23565b50505b9250929050565b6001600160a01b0382811660009081526140ac6020908152604080832093851683526003840182528083208151808301909252546bffffffffffffffffffffffff811682526c01000000000000000000000000900461ffff169181018290529192839290918390610b9b8842610f12565b835191029150610bba9082906bffffffffffffffffffffffff16615ad9565b6001840154602084015191965061ffff9081169116811115610c29576020830151600185015461ffff9091168203955063ffffffff660100000000000082048116916201000090041680821115610c2157808203915081871115610c1c578196505b610c26565b600096505b50505b505050509250929050565b610c678360008469ffffffffffffffffffff168461ffff1642426000801b60405180602001604052806000815250613102565b505050565b6001600160a01b0380831660009081526140ac602090815260408083209385168352600390930190529081208054909161ffff6c01000000000000000000000000830416916bffffffffffffffffffffffff1690610cca8642610f12565b90508281026000610cdb8285615ad9565b905080600003610d17576040517f433b2e9500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff8316178655610d5b878262033450613718565b866001600160a01b0316886001600160a01b03167fd9f7a958c85633cf171f617fa7127c9f19bacb103e2bce41bb6728d0476515808386604051610da9929190918252602082015260400190565b60405180910390a35050505050505050565b808015801590610dcf5750610dcf81421190565b15610e06576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82600003610e1657429250610e56565b610e1f83421190565b15610e56576040517fa619834a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e788787878787886000801b60405180602001604052806000815250613102565b50505050505050565b6040517f40c1a064000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b038416906340c1a06490619c40906024016020604051808303818786fa158015610ee6573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610f0b9190615aec565b9392505050565b6001600160a01b03821660009081526140ac60205260408120600181015463ffffffff660100000000000082048116620100009092041610610f765760018101546a010000000000000000000090046bffffffffffffffffffffffff169150610fef565b8054610fec906bffffffffffffffffffffffff808216916c0100000000000000000000000081049091169063ffffffff780100000000000000000000000000000000000000000000000082048116917c010000000000000000000000000000000000000000000000000000000090041687613863565b91505b5092915050565b6000610f0b83836138ba565b866110366001600160a01b0382167f5bf6f7b800000000000000000000000000000000000000000000000000000000613961565b61106c576040517f17fdef0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61109f6001600160a01b0382167f80ac58cd00000000000000000000000000000000000000000000000000000000613037565b6110d5576040517f29d190d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a660048201523060248201526001600160a01b038216906391d1485490604401602060405180830381865afa158015611158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117c9190615b09565b6111b2576040517f436d641100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b876001600160a01b0381166391d1485460006111cc61397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381865afa15801561122e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112529190615b09565b611288576040517f5fad784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b886112928161398c565b156112c9576040517f2540293000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b868811611302576040517f5a42b07900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8560000361133c576040517f92865bfe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460000361134c5742945061138c565b61135585421190565b1561138c576040517f3a3baeca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b836000036113c6576040517f3335f9a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006113d28587615b2b565b90506303c267004201811115611421576040517f82fae596000000000000000000000000000000000000000000000000000000008152426303c267000160048201526024015b60405180910390fd5b60008b6001600160a01b0316638ae3e5f16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611461573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114859190615b3e565b9050806000036114c1576040517f61b9c86b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006114cb61397d565b6001600160a01b038e1660009081526140ac602052604090209091506114f08c613997565b815477ffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff878116919091027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff16919091177801000000000000000000000000000000000000000000000000918c1691909102177fffffffffffffffff000000000000000000000000000000000000000000000000166c010000000000000000000000006bffffffffffffffffffffffff8e8116919091027fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169190911791161781556115f68a613a39565b6115ff84613acd565b6002830180546001600160a01b0386167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617905560018301805461ffff9093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000063ffffffff909316660100000000000002929092167fffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff0000909316929092171790556116ad8e8e613b63565b6040517f24ef95e7000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b038f16906324ef95e790602401600060405180830381600087803b15801561170857600080fd5b505af115801561171c573d6000803e3d6000fd5b50505050816001600160a01b03168e6001600160a01b03167f4be408b58dfcdd9eed5e094c36b581760de5ad0b07dc4fc948b61fba5001898e8e8e8e8e8a604051611789959493929190948552602085019390935260408401919091526060830152608082015260a00190565b60405180910390a35050505050505050505050505050565b600054610100900460ff16158080156117c15750600054600160ff909116105b806117db5750303b1580156117db575060005460ff166001145b611867576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401611418565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156118c557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6118cd613d8d565b801561193057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b808015801590611947575061194781421190565b1561197e576040517fbcb9700400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848484826119b8576040517f229955da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008190036119f3576040517fe85bdf0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b88600003611a0357429850611a43565b611a0c89421190565b15611a43576040517fdddbb7cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b898910611a7c576040517f77b2af4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ac28e8e8e8e8e8e8e8e8e8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061310292505050565b5050505050505050505050505050565b611ae185600086868686610dbb565b5050505050565b600080606080600080611afb8989613e2e565b9050611b0c8989838a600080613ef1565b5093995091965094509250600090505b8351811015611b4e57838181518110611b3757611b376158f3565b602002602001015186019550806001019050611b1c565b5093975093979195509350565b600080600080611b6a86611d3f565b5050945094509450505080611b855760009350505050611bda565b6001600160a01b038087166000908152613cc3602090815260408083209389168352600190930190522054838110611bc4576000945050505050611bda565b808403945082851115611bd5578294505b505050505b92915050565b6001600160a01b0385166000908152613cc360205260408120600281015463ffffffff16421015611d28576002810154640100000000900463ffffffff16421015611cba57600281015463ffffffff8082166401000000009092041603611c73576040517f77b2af4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028101546040517f8ba20e6800000000000000000000000000000000000000000000000000000000815264010000000090910463ffffffff166004820152602401611418565b6000611cd98585611cc961397d565b6001600160a01b0316919061459d565b600081815260038401602052604090205490915060ff16611d26576040517ff88937c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b611d34818888886145f9565b979650505050505050565b6000806000806000806000876001600160a01b0316638ae3e5f16040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611da4575060408051601f3d908101601f19168201909252611da191810190615b3e565b60015b15611ee7578015611ee5576040517f91d148540000000000000000000000000000000000000000000000000000000081527f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a660048201523060248201526001600160a01b038a16906391d1485490604401602060405180830381865afa925050508015611e4e575060408051601f3d908101601f19168201909252611e4b91810190615b09565b60015b15611ee5576001600160a01b038a81166000908152613cc36020526040902080546002909101549181169a5074010000000000000000000000000000000000000000810469ffffffffffffffffffff1699507e01000000000000000000000000000000000000000000000000000000000000900461ffff169750919550935063ffffffff8082169350640100000000909104169050835b505b919395979092949650565b600081600003611f2e576040517fb3896a5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660009081526140ac60205260408120805490917c010000000000000000000000000000000000000000000000000000000090910463ffffffff169003611faa576040517fcc4f974c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547801000000000000000000000000000000000000000000000000900463ffffffff1642101561202d5780546040517f70fdd722000000000000000000000000000000000000000000000000000000008152780100000000000000000000000000000000000000000000000090910463ffffffff166004820152602401611418565b600061203761397d565b6001600160a01b03811660009081526003840160205260408120805492935091612075906c01000000000000000000000000900461ffff1687615b2b565b600185015490915061ffff168111156120df57815460018501546040517fb3c84a0b0000000000000000000000000000000000000000000000000000000081526c0100000000000000000000000090920461ffff9081166004840152166024820152604401611418565b60006120eb8842610f12565b600186015490915087820290760100000000000000000000000000000000000000000000810460ff169061212b9062010000900463ffffffff168a615b2b565b60018801805463ffffffff90921662010000027fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff6bffffffffffffffffffffffff8088166a010000000000000000000002919091167fffffffffffffffffffff000000000000000000000000ffffffff00000000ffff9094169390931717905585546121c1916121bc911684615b2b565b613997565b85547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000166c0100000000000000000000000061ffff8716027fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016176bffffffffffffffffffffffff9190911617855561223b82600161491d565b6040517fd115124900000000000000000000000000000000000000000000000000000000815261ffff8a1660048201526001600160a01b0387811660248301528b169063d1151249906044016020604051808303816000875af11580156122a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ca9190615b3e565b975080156122f15760028701546122ee908b9084906001600160a01b03168b614a07565b50505b60408051848152602081018b90529081018990526001600160a01b0380881691908c16907fd63692b8fbf23357add4e0ac130de567b601498f82505d23b329b450c74945059060600160405180910390a35050505050505092915050565b6001600160a01b0383166000908152613cc360205260408120600281015463ffffffff164210156123ba5760028101546040517f26763e5a00000000000000000000000000000000000000000000000000000000815263ffffffff9091166004820152602401611418565b6123ca81868661ffff16866145f9565b95945050505050565b6040517fde5488af0000000000000000000000000000000000000000000000000000000081526001600160a01b03848116600483015260609182917f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d169063de5488af90619c40906024016020604051808303818786fa93505050508015612478575060408051601f3d908101601f1916820190925261247591810190615aec565b60015b156126c957856001600160a01b0316816001600160a01b0316146126c7579450846124cc6001600160a01b0382167f2a55205a00000000000000000000000000000000000000000000000000000000613037565b156125d7576040517f2a55205a0000000000000000000000000000000000000000000000000000000081526004810186905261271060248201526001600160a01b03871690632a55205a90619c409060440160408051808303818786fa93505050508015612557575060408051601f3d908101601f1916820190925261255491810190615896565b60015b156125d75780156125d45760408051600180825281830190925290602080830190803683370190505094508185600081518110612596576125966158f3565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337019050509350505050612993565b50505b825115801561261457506126146001600160a01b0387167fbb3bafd600000000000000000000000000000000000000000000000000000000613037565b156126c7576040517fbb3bafd6000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b0387169063bb3bafd690619c40906024016000604051808303818786fa9350505050801561269d57506040513d6000823e601f3d908101601f1916820160405261269a9190810190615a46565b60015b156126c7578151158015906126b3575080518251145b156126c45790935091506129939050565b50505b505b6126fc6001600160a01b0386167fb779958400000000000000000000000000000000000000000000000000000000613037565b15612831576040517fb9c4d9fb000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b0386169063b9c4d9fb90619c40906024016000604051808303818786fa9350505050801561278557506040513d6000823e601f3d908101601f191682016040526127829190810190615b57565b60015b156128315780511561282f576040517f0ebd4c7f000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03871690630ebd4c7f90619c40906024016000604051808303818786fa9350505050801561281557506040513d6000823e601f3d908101601f191682016040526128129190810190615b8c565b60015b1561282f57805182510361282d579092509050612993565b505b505b6001600160a01b038316156128af5760408051600180825281830190925290602080830190803683370190505091508282600081518110612874576128746158f3565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337019050509050612993565b846001600160a01b0316638da5cb5b619c406040518263ffffffff1660e01b81526004016020604051808303818786fa9350505050801561290d575060408051601f3d908101601f1916820190925261290a91810190615aec565b60015b15612993576001600160a01b038116156129915760408051600180825281830190925290602080830190803683370190505092508083600081518110612955576129556158f3565b6001600160a01b039290921660209283029190910182015260408051600180825281830190925291828101908036833701905050915050612993565b505b935093915050565b6001600160a01b03811660009081526140ac60205260408120805460018201546bffffffffffffffffffffffff808316946c01000000000000000000000000840482169461ffff84169463ffffffff780100000000000000000000000000000000000000000000000082048116957c010000000000000000000000000000000000000000000000000000000090920481169466010000000000008304821694620100008404909216936a01000000000000000000009093041691612a5f8b42610f12565b9150509193959799909294969850565b612a828960008a8a8a8a8a8a8a8a611933565b505050505050505050565b83806001600160a01b0316638ae3e5f16040518163ffffffff1660e01b8152600401602060405180830381865afa158015612acc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612af09190615b3e565b600003612b29576040517f3c263ade00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846001600160a01b0381166391d148546000612b4361397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381865afa158015612ba5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bc99190615b09565b612bff576040517f5fad784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84848482612c39576040517f229955da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000819003612c74576040517fe85bdf0000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0389166000908152613cc360205260409020600281015463ffffffff164210612d115780546001600160a01b0316612cdf576040517f974bcaec00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517ff2279aef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281015463ffffffff64010000000082048116911603612d5e576040517f77b2af4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008981526003820160205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055516001600160a01b038b16907f7f11be7894109a714225fbb6c33d88a14582407d653ac3fc5abf07d9b3ce891490612dd6908c908c908c90615bc1565b60405180910390a250505050505050505050565b6001600160a01b0380821660009081526140ac60205260409020600281015490911680612e43576040517fcc4f974c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001820154760100000000000000000000000000000000000000000000900460ff1615612e9c576040517fba5a58b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600182015482546201000090910463ffffffff908116917c01000000000000000000000000000000000000000000000000000000009004164211158015612ef6575060018301546601000000000000900463ffffffff1681105b15612f7257825460018401546040517f1dd4212a0000000000000000000000000000000000000000000000000000000081527c010000000000000000000000000000000000000000000000000000000090920463ffffffff90811660048401526601000000000000909104168290036024820152604401611418565b6000612f7e8542610f12565b6001850180547fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000001790559050818102600080612fd588848884614a07565b60408051878152602081018990529081018390526060810182905291935091506001600160a01b038916907f5e16e96b4ba4fe46f3be73d54d1fa0da481494ab74c2d6e33328366d6437693c9060800160405180910390a25050505050505050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d915060005190508280156130ef575060208210155b8015611d34575015159695505050505050565b876131366001600160a01b0382167f5bf6f7b800000000000000000000000000000000000000000000000000000000613961565b61316c576040517f17fdef0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61319f6001600160a01b0382167f80ac58cd00000000000000000000000000000000000000000000000000000000613037565b6131d5576040517f29d190d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a660048201523060248201526001600160a01b038216906391d1485490604401602060405180830381865afa158015613258573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061327c9190615b09565b6132b2576040517f436d641100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b88806001600160a01b0316638ae3e5f16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133159190615b3e565b60000361334e576040517f3c263ade00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b896001600160a01b0381166391d14854600061336861397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381865afa1580156133ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ee9190615b09565b613424576040517f5fad784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b866303c26700420181111561346d576040517fe43fc8b2000000000000000000000000000000000000000000000000000000008152426303c26700016004820152602401611418565b8b6134778161398c565b156134ae576040517f2540293000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b896000036134e8576040517f250099ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038d166000908152613cc3602052604081209061350a61397d565b82547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03821617835590506135468d614a3a565b825469ffffffffffffffffffff9190911674010000000000000000000000000000000000000000027fffff00000000000000000000ffffffffffffffffffffffffffffffffffffffff90911617825561359e8c613a39565b825461ffff919091167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116178255428b14613627576002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff8d161790555b428a14613668576002820180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1664010000000063ffffffff8d16021790555b88156136a8576000898152600383016020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555b6136b28f8f613b63565b806001600160a01b03168f6001600160a01b03167fa4e684574cd21d7eb4df36ec6d7f86d16aa900ceddae41e4e82f8f4170f293b98f8f8f8f8f8f6040516136ff96959493929190615c1b565b60405180910390a3505050505050505050505050505050565b8160000361372557505050565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d8060008114613775576040519150601f19603f3d011682016040523d82523d6000602084013e61377a565b606091505b505090508061385d576040517faa67c9190000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063aa67c9199085906024016000604051808303818588803b15801561380057600080fd5b505af1158015613814573d6000803e3d6000fd5b5050505050836001600160a01b03167fa2201512569adb2d513531dfd69b66df50bd5cffb8c1bbe65a4611f9e1eadbd18460405161385491815260200190565b60405180910390a25b50505050565b60008382116138735750846123ca565b8282106138815750836123ca565b61388b8587615ad9565b905081830361389a8183615c71565b915084840382816138ad576138ad615c88565b049150611d348683615b2b565b6040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa925050508015613937575060408051601f3d908101601f1916820190925261393491810190615aec565b60015b15613957576001600160a01b03811615613955576000915050611bda565b505b610f0b8383614ad6565b600061396c83614b04565b8015610f0b5750610f0b8383613037565b6000613987614b68565b905090565b6000611bda82614bc5565b60006bffffffffffffffffffffffff821115613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401611418565b5090565b600061ffff821115613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401611418565b600063ffffffff821115613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f32206269747300000000000000000000000000000000000000000000000000006064820152608401611418565b8015613d89577f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f6001600160a01b0316632e06db9682613ba161397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815260048101929092526001600160a01b03166024820152604401602060405180830381865afa158015613c03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c279190615b09565b613d44576040517f55daed3e000000000000000000000000000000000000000000000000000000008152600481018290526000907f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f6001600160a01b0316906355daed3e906024016040805180830381865afa158015613cab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ccf9190615cb7565b5090506001600160a01b038116613d12576040517f167ce11a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa0cfce1100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660008181526138db6020526040808220849055518392917fa69ac11ca23c60fc41e8e7b0217b2da8c6290a60e39202b1a24b126f47480b4791a35b5050565b600054610100900460ff16613e24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401611418565b613e2c614bf7565b565b6040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa925050508015613eab575060408051601f3d908101601f19168201909252613ea891810190615aec565b60015b15613957576001600160a01b03811615613955576040517f5ca60e0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006060806000806000612710613f078d614c96565b613f11908b615c71565b613f1b9190615ce6565b6040517f4c542f770000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e90529197506000917f0000000000000000000000007d1962f50848b03f6d1d05e4f15cc452b0dada9d1690634c542f7790604401602060405180830381865afa925050508015613fc3575060408051601f3d908101601f19168201909252613fc091810190615aec565b60015b15613fcb5790505b6040517f0d7daf3e0000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e90527f0000000000000000000000007d1962f50848b03f6d1d05e4f15cc452b0dada9d1690630d7daf3e90604401600060405180830381865afa92505050801561407257506040513d6000823e601f3d908101601f1916820160405261406f9190810190615a46565b60015b1561407d5790965094505b8551600003614140576040517fefef76f80000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e905282811660448301527f0000000000000000000000007d1962f50848b03f6d1d05e4f15cc452b0dada9d169063efef76f890606401600060405180830381865afa92505050801561413557506040513d6000823e601f3d908101601f191682016040526141329190810190615a46565b60015b156141405790965094505b855115158061416c57507f00000000000000000000000000000000000000000000000000000000000000015b156144d65760007f00000000000000000000000000000000000000000000000000000000000000011561421757878b03905086516000036142125760408051600180825281830190925290602080830190803683370190505096508b876000815181106141db576141db6158f3565b6001600160a01b03929092166020928302919091018201526040805160018082528183019092529182810190803683370190505095505b614298565b816001600160a01b03168c6001600160a01b0316148061426d575086511580159061426d575086600081518110614250576142506158f3565b60200260200101516001600160a01b03168c6001600160a01b0316145b1561427b5750868a03614298565b50600a8a048061428b898d615ad9565b6142959190615ad9565b94505b6142a3876005614ca1565b6142ae866005614ca1565b61ffff8916156142fb576127106142c961ffff8b168d615c71565b6142d39190615ce6565b9250846000036142ee576142e78382615ad9565b90506142fb565b6142f88386615ad9565b94505b600060018851111561442f5760005b88518110156143e7578d6001600160a01b031689828151811061432f5761432f6158f3565b60200260200101516001600160a01b03160361434e5760009692909201915b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82146143df5761271088828151811061438a5761438a6158f3565b602002602001015111156143c0577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff91506143df565b8781815181106143d2576143d26158f3565b6020026020010151820191505b60010161430a565b5080158061441457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81145b1561442f57614424886001614ca1565b61442f876001614ca1565b600060015b89518110156144a4576000838a8381518110614452576144526158f3565b6020026020010151866144659190615c71565b61446f9190615ce6565b905061447b8184615b2b565b9250808a8381518110614490576144906158f3565b602090810291909101015250600101614434565b506144af8184615ad9565b886000815181106144c2576144c26158f3565b60200260200101818152505050505061450f565b868a03935061ffff88161561450f576127106144f661ffff8a168c615c71565b6145009190615ce6565b915061450c8285615ad9565b93505b6001600160a01b03891615801590614540575061452a61397d565b6001600160a01b0316896001600160a01b031614155b801561455e57508a6001600160a01b0316896001600160a01b031614155b801561457c5750806001600160a01b0316896001600160a01b031614155b1561458d5760648a04925082870396505b5096509650965096509650969050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085901b166020820152600090819060340160408051601f19818403018152919052805160209091012090506123ca848483614cae565b600082600003614635576040517f2d69aaf400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061463f61397d565b6001600160a01b038116600090815260018801602052604081205491925090614669908690615b2b565b87549091507e01000000000000000000000000000000000000000000000000000000000000900461ffff168111156147585786547e01000000000000000000000000000000000000000000000000000000000000900461ffff166000036146fc576040517f974bcaec00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86546040517ffa7c028e0000000000000000000000000000000000000000000000000000000081527e0100000000000000000000000000000000000000000000000000000000000090910461ffff166004820152602401611418565b6001600160a01b03821660009081526001880160205260409020819055865474010000000000000000000000000000000000000000900469ffffffffffffffffffff168502348110156147da576040517f9239ca9d00000000000000000000000000000000000000000000000000000000815260048101829052602401611418565b6147e581600061491d565b6040517fd115124900000000000000000000000000000000000000000000000000000000815261ffff871660048201526001600160a01b03848116602483015288169063d1151249906044016020604051808303816000875af1158015614850573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148749190615b3e565b935060008061488289614cfa565b8b54919350915060009081906148a8908c908a906001600160a01b0316888d8989614dc9565b509150915087876001600160a01b03168c6001600160a01b03167f05ebbb6b0ce7d564230ba625dd7a0e5108786b0852d6060de6099e1778203e348d8686604051614906939291909283526020830191909152604082015260600190565b60405180910390a450505050505050949350505050565b348211156149d9577f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436001600160a01b031663452f2b8f61495c61397d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b0390911660048201523485036024820152604401600060405180830381600087803b1580156149bd57600080fd5b505af11580156149d1573d6000803e3d6000fd5b505050505050565b8080156149e557503482105b15613d8957613d898234036149f861397d565b6001600160a01b031690614ff0565b600080600080614a1688614cfa565b91509150614a2a8886888a60008787614dc9565b5090999098509650505050505050565b600069ffffffffffffffffffff821115613a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201527f30206269747300000000000000000000000000000000000000000000000000006064820152608401611418565b6001600160a01b0380831660009081526140ac60205260409020600201541680611bda57610f0b838361513d565b6000614b30827f01ffc9a700000000000000000000000000000000000000000000000000000000613037565b8015611bda5750614b61827fffffffff00000000000000000000000000000000000000000000000000000000613037565b1592915050565b336001600160a01b037f000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e3168103614bc257507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec36013560601c5b90565b6001600160a01b0381811660009081526140ac6020526040812060020154909116151580611bda5750611bda82615168565b600054610100900460ff16614c8e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401611418565b6001610dad55565b6000611bda82615195565b8082511115613d89579052565b600081815b84811015614cf157614cdd82878784818110614cd157614cd16158f3565b9050602002013561527b565b915080614ce981615d21565b915050614cb3565b50949350505050565b6001600160a01b03811660009081526138db602052604081205481908015614dc3576040517f55daed3e000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f6001600160a01b0316906355daed3e906024016040805180830381865afa158015614d99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614dbd9190615cb7565b90935091505b50915091565b600080600086600003614de457506000915081905080614fe3565b606080600080614df88e8e8e8e8e8d613ef1565b8451959c509199509297509095509093509150600090600114614e1d57614e20614e22565b620334505b905060005b8551811015614e9657614e6d868281518110614e4557614e456158f3565b6020026020010151868381518110614e5f57614e5f6158f3565b602002602001015184613718565b848181518110614e7f57614e7f6158f3565b602002602001015188019750806001019050614e27565b50614ea48d87614e20613718565b614ed17f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb689614e20613718565b8215614f4c57614ee48b84614e20613718565b8d8f6001600160a01b03167f141b92fd9766c80ab120598ea2f6be9802470ec59b5446dd9bf46214ead8d08e8d866000604051614f3f939291906001600160a01b039390931683526020830191909152604082015260600190565b60405180910390a3968201965b6001600160a01b038a1615614fdd578115614f845785600003614f725795810195614f77565b948101945b614f848a83614e20613718565b8d8f6001600160a01b03167f27a4dd4ff659a9e6354fb079b2208365e5b83f55c22a4150eee2bca89501cb988c85604051614fd49291906001600160a01b03929092168252602082015260400190565b60405180910390a35b50505050505b9750975097945050505050565b8047101561505a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401611418565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146150a7576040519150601f19603f3d011682016040523d82523d6000602084013e6150ac565b606091505b5050905080610c67576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401611418565b600061514883611d3f565b50949550506001600160a01b0385169350611bda92505050576000610f0b565b6001600160a01b038181166000908152613cc36020526040812054909116151580611bda57506000611bda565b6000816001600160a01b031663dfea951d6040518163ffffffff1660e01b8152600401600060405180830381865afa9250505080156151f657506040513d6000823e601f3d908101601f191682016040526151f39190810190615d59565b60015b15615255576040517fee2afa3f960e108aca17013728aafa363a0f4485661d9b6f41c6b4ddb55008ee9061522e908390602001615de4565b604051602081830303815290604052805190602001200361525357506101f492915050565b505b7f00000000000000000000000000000000000000000000000000000000000005dc611bda565b6000818310615297576000828152602084905260409020610f0b565b6000838152602083905260409020610f0b565b6001600160a01b038116811461193057600080fd5b6000602082840312156152d157600080fd5b8135610f0b816152aa565b600080604083850312156152ef57600080fd5b82356152fa816152aa565b946020939093013593505050565b600081518084526020808501945080840160005b838110156153415781516001600160a01b03168752958201959082019060010161531c565b509495945050505050565b600081518084526020808501945080840160005b8381101561534157815187529582019590820190600101615360565b60408152600061538f6040830185615308565b82810360208401526123ca818561534c565b600080604083850312156153b457600080fd5b82356153bf816152aa565b915060208301356153cf816152aa565b809150509250929050565b61ffff8116811461193057600080fd5b6000806000606084860312156153ff57600080fd5b833561540a816152aa565b9250602084013569ffffffffffffffffffff8116811461542957600080fd5b91506040840135615439816153da565b809150509250925092565b60008060008060008060c0878903121561545d57600080fd5b8635615468816152aa565b9860208801359850604088013597606081013597506080810135965060a00135945092505050565b600080600080600080600060e0888a0312156154ab57600080fd5b87356154b6816152aa565b9960208901359950604089013598606081013598506080810135975060a0810135965060c00135945092505050565b60008083601f8401126154f757600080fd5b50813567ffffffffffffffff81111561550f57600080fd5b602083019150836020828501011115610b2357600080fd5b6000806000806000806000806000806101208b8d03121561554757600080fd5b8a35615552816152aa565b995060208b0135985060408b0135975060608b0135965060808b0135955060a08b0135945060c08b0135935060e08b013567ffffffffffffffff81111561559857600080fd5b6155a48d828e016154e5565b915080945050809250506101008b013590509295989b9194979a5092959850565b600080600080600060a086880312156155dd57600080fd5b85356155e8816152aa565b97602087013597506040870135966060810135965060800135945092505050565b60008060006060848603121561561e57600080fd5b8335615629816152aa565b95602085013595506040909401359392505050565b86815285602082015260c06040820152600061565d60c0830187615308565b828103606084015261566f818761534c565b9150508360808301526001600160a01b03831660a0830152979650505050505050565b6000806000806000608086880312156156aa57600080fd5b85356156b5816152aa565b94506020860135935060408601356156cc816152aa565b9250606086013567ffffffffffffffff808211156156e957600080fd5b818801915088601f8301126156fd57600080fd5b81358181111561570c57600080fd5b8960208260051b850101111561572157600080fd5b9699959850939650602001949392505050565b60008060006060848603121561574957600080fd5b8335615754816152aa565b92506020840135615764816153da565b91506040840135615439816152aa565b60008060006060848603121561578957600080fd5b8335615794816152aa565b9250602084013591506040840135615439816152aa565b60008060008060008060008060006101008a8c0312156157ca57600080fd5b89356157d5816152aa565b985060208a0135975060408a0135965060608a0135955060808a0135945060a08a0135935060c08a013567ffffffffffffffff81111561581457600080fd5b6158208c828d016154e5565b9a9d999c50979a9699959894979660e00135949350505050565b6000806000806060858703121561585057600080fd5b843561585b816152aa565b935060208501359250604085013567ffffffffffffffff81111561587e57600080fd5b61588a878288016154e5565b95989497509550505050565b600080604083850312156158a957600080fd5b82516158b4816152aa565b6020939093015192949293505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561594b5761594b6158c4565b604052919050565b600067ffffffffffffffff82111561596d5761596d6158c4565b5060051b60200190565b600082601f83011261598857600080fd5b8151602061599d61599883615953565b615922565b82815260059290921b840181019181810190868411156159bc57600080fd5b8286015b848110156159e05780516159d3816152aa565b83529183019183016159c0565b509695505050505050565b600082601f8301126159fc57600080fd5b81516020615a0c61599883615953565b82815260059290921b84018101918181019086841115615a2b57600080fd5b8286015b848110156159e05780518352918301918301615a2f565b60008060408385031215615a5957600080fd5b825167ffffffffffffffff80821115615a7157600080fd5b615a7d86838701615977565b93506020850151915080821115615a9357600080fd5b50615aa0858286016159eb565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115611bda57611bda615aaa565b600060208284031215615afe57600080fd5b8151610f0b816152aa565b600060208284031215615b1b57600080fd5b81518015158114610f0b57600080fd5b80820180821115611bda57611bda615aaa565b600060208284031215615b5057600080fd5b5051919050565b600060208284031215615b6957600080fd5b815167ffffffffffffffff811115615b8057600080fd5b610fec84828501615977565b600060208284031215615b9e57600080fd5b815167ffffffffffffffff811115615bb557600080fd5b610fec848285016159eb565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b60005b83811015615c12578181015183820152602001615bfa565b50506000910152565b86815285602082015284604082015283606082015282608082015260c060a0820152600082518060c0840152615c588160e0850160208701615bf7565b601f01601f19169190910160e001979650505050505050565b8082028115828204841417611bda57611bda615aaa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008060408385031215615cca57600080fd5b8251615cd5816152aa565b60208401519092506153cf816153da565b600082615d1c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615d5257615d52615aaa565b5060010190565b600060208284031215615d6b57600080fd5b815167ffffffffffffffff80821115615d8357600080fd5b818401915084601f830112615d9757600080fd5b815181811115615da957615da96158c4565b615dbc6020601f19601f84011601615922565b9150808252856020828501011115615dd357600080fd5b614cf1816020840160208601615bf7565b60008251615df6818460208701615bf7565b919091019291505056fea26469706673582212204ecb477e302353cd291bd1cf37495d404024a009e937ad1f4a9eb6c5b82f738264736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb600000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e3
-----Decoded View---------------
Arg [0] : treasury (address): 0x67Df244584b67E8C51B10aD610aAfFa9a402FdB6
Arg [1] : feth (address): 0x49128CF8ABE9071ee24540a296b5DED3F9D50443
Arg [2] : royaltyRegistry (address): 0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D
Arg [3] : nftMarket (address): 0xcDA72070E455bb31C7690a170224Ce43623d0B6f
Arg [4] : router (address): 0x762340B8a40Cdd5BFC3eDD94265899FDa345D0E3
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb6
Arg [1] : 00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443
Arg [2] : 000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d
Arg [3] : 000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f
Arg [4] : 000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e3
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.