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
|
|||
---|---|---|---|---|---|---|
16749658 | 653 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
NFTMarket
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 10000 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.12; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./mixins/shared/Constants.sol"; import "./mixins/shared/FETHNode.sol"; import "./mixins/shared/FoundationTreasuryNode.sol"; import "./mixins/shared/MarketFees.sol"; import "./mixins/shared/MarketSharedCore.sol"; import "./mixins/shared/RouterContext.sol"; import "./mixins/shared/SendValueWithFallbackWithdraw.sol"; import "./mixins/nftMarket/NFTMarketAuction.sol"; import "./mixins/nftMarket/NFTMarketBuyPrice.sol"; import "./mixins/nftMarket/NFTMarketCore.sol"; import "./mixins/nftMarket/NFTMarketOffer.sol"; import "./mixins/nftMarket/NFTMarketPrivateSaleGap.sol"; import "./mixins/nftMarket/NFTMarketReserveAuction.sol"; import "./mixins/nftMarket/NFTMarketExhibition.sol"; /** * @title A market for NFTs on Foundation. * @notice The Foundation marketplace is a contract which allows traders to buy and sell NFTs. * It supports buying and selling via auctions, private sales, buy price, and offers. * @dev All sales in the Foundation market will pay the creator 10% royalties on secondary sales. This is not specific * to NFTs minted on Foundation, it should work for any NFT. If royalty information was not defined when the NFT was * originally deployed, it may be added using the [Royalty Registry](https://royaltyregistry.xyz/) which will be * respected by our market contract. * @author batu-inal & HardlyDifficult */ contract NFTMarket is Initializable, FoundationTreasuryNode, Context, RouterContext, FETHNode, MarketSharedCore, NFTMarketCore, ReentrancyGuardUpgradeable, SendValueWithFallbackWithdraw, MarketFees, NFTMarketExhibition, NFTMarketAuction, NFTMarketReserveAuction, NFTMarketPrivateSaleGap, NFTMarketBuyPrice, NFTMarketOffer { /** * @notice Set immutable variables for the implementation contract. * @dev Using immutable instead of constants allows us to use different values on testnet. * @param treasury The Foundation Treasury contract address. * @param feth The FETH ERC-20 token contract address. * @param royaltyRegistry The Royalty Registry contract address. * @param duration The duration of the auction in seconds. */ constructor( address payable treasury, address feth, address royaltyRegistry, uint256 duration, address router ) FoundationTreasuryNode(treasury) FETHNode(feth) MarketFees( /* protocolFeeInBasisPoints: */ 500, royaltyRegistry, /* assumePrimarySale: */ false ) NFTMarketReserveAuction(duration) 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 { NFTMarketAuction._initializeNFTMarketAuction(); } /** * @inheritdoc NFTMarketCore */ function _beforeAuctionStarted( address nftContract, uint256 tokenId ) internal override(NFTMarketCore, NFTMarketBuyPrice, NFTMarketOffer) { // This is a no-op function required to avoid compile errors. super._beforeAuctionStarted(nftContract, tokenId); } /** * @inheritdoc NFTMarketCore */ function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address authorizeSeller ) internal override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) { // This is a no-op function required to avoid compile errors. super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller); } /** * @inheritdoc NFTMarketCore */ function _transferFromEscrowIfAvailable( address nftContract, uint256 tokenId, address recipient ) internal override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) { // This is a no-op function required to avoid compile errors. super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient); } /** * @inheritdoc NFTMarketCore */ function _transferToEscrow( address nftContract, uint256 tokenId ) internal override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) { // This is a no-op function required to avoid compile errors. super._transferToEscrow(nftContract, tokenId); } /** * @inheritdoc MarketSharedCore */ function _getSellerOf( address nftContract, uint256 tokenId ) internal view override(MarketSharedCore, NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) returns (address payable seller) { // This is a no-op function required to avoid compile errors. seller = super._getSellerOf(nftContract, tokenId); } /** * @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 (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/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}. * 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 OR Apache-2.0 pragma solidity ^0.8.12; /** * @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.12; /** * @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.12; /** * @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.12; /** * @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.12; /** * @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.12; /** * @title Interface for routing calls to the NFT Market to set buy now prices. * @author HardlyDifficult */ interface INFTMarketBuyNow { function setBuyPrice(address nftContract, uint256 tokenId, uint256 price) external; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; /** * @title Interface for routing calls to the NFT Market to create reserve auctions. * @author HardlyDifficult */ interface INFTMarketReserveAuction { function createReserveAuctionV2( address nftContract, uint256 tokenId, uint256 reservePrice, uint256 exhibitionId ) external returns (uint256 auctionId); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; /** * @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.12; 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.12; 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.12; /** * @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.12; 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.12; /** * @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.12; /** * @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.12; /** * @title An abstraction layer for auctions. * @dev This contract can be expanded with reusable calls and data as more auction types are added. * @author batu-inal & HardlyDifficult */ abstract contract NFTMarketAuction { /** * @notice A global id for auctions of any type. */ uint256 private nextAuctionId; /** * @notice Called once to configure the contract after the initial proxy deployment. * @dev This sets the initial auction id to 1, making the first auction cheaper * and id 0 represents no auction found. */ function _initializeNFTMarketAuction() internal { nextAuctionId = 1; } /** * @notice Returns id to assign to the next auction. */ function _getNextAndIncrementAuctionId() internal returns (uint256) { // AuctionId cannot overflow 256 bits. unchecked { return nextAuctionId++; } } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[1_000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "../../interfaces/internal/routes/INFTMarketBuyNow.sol"; import "../shared/MarketFees.sol"; import "../shared/FoundationTreasuryNode.sol"; import "../shared/FETHNode.sol"; import "../shared/MarketSharedCore.sol"; import "../shared/SendValueWithFallbackWithdraw.sol"; import "./NFTMarketCore.sol"; /// @param buyPrice The current buy price set for this NFT. error NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price(uint256 buyPrice); error NFTMarketBuyPrice_Cannot_Buy_Unset_Price(); error NFTMarketBuyPrice_Cannot_Cancel_Unset_Price(); /// @param owner The current owner of this NFT. error NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price(address owner); /// @param owner The current owner of this NFT. error NFTMarketBuyPrice_Only_Owner_Can_Set_Price(address owner); error NFTMarketBuyPrice_Price_Already_Set(); error NFTMarketBuyPrice_Price_Too_High(); /// @param seller The current owner of this NFT. error NFTMarketBuyPrice_Seller_Mismatch(address seller); /** * @title Allows sellers to set a buy price of their NFTs that may be accepted and instantly transferred to the buyer. * @notice NFTs with a buy price set are escrowed in the market contract. * @author batu-inal & HardlyDifficult */ abstract contract NFTMarketBuyPrice is INFTMarketBuyNow, Initializable, FoundationTreasuryNode, Context, FETHNode, MarketSharedCore, NFTMarketCore, ReentrancyGuardUpgradeable, SendValueWithFallbackWithdraw, MarketFees { using AddressUpgradeable for address payable; /// @notice Stores the buy price details for a specific NFT. /// @dev The struct is packed into a single slot to optimize gas. struct BuyPrice { /// @notice The current owner of this NFT which set a buy price. /// @dev A zero price is acceptable so a non-zero address determines whether a price has been set. address payable seller; /// @notice The current buy price set for this NFT. uint96 price; } /// @notice Stores the current buy price for each NFT. mapping(address => mapping(uint256 => BuyPrice)) private nftContractToTokenIdToBuyPrice; /** * @notice Emitted when an NFT is bought by accepting the buy price, * indicating that the NFT has been transferred and revenue from the sale distributed. * @dev The total buy price that was accepted is `totalFees` + `creatorRev` + `sellerRev`. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param buyer The address of the collector that purchased the NFT using `buy`. * @param seller The address of the seller which originally set the buy price. * @param 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. * @param sellerRev The amount of ETH that was sent to the owner for this sale. */ event BuyPriceAccepted( address indexed nftContract, uint256 indexed tokenId, address indexed seller, address buyer, uint256 totalFees, uint256 creatorRev, uint256 sellerRev ); /** * @notice Emitted when the buy price is removed by the owner of an NFT. * @dev The NFT is transferred back to the owner unless it's still escrowed for another market tool, * e.g. listed for sale in an auction. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. */ event BuyPriceCanceled(address indexed nftContract, uint256 indexed tokenId); /** * @notice Emitted when a buy price is invalidated due to other market activity. * @dev This occurs when the buy price is no longer eligible to be accepted, * e.g. when a bid is placed in an auction for this NFT. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. */ event BuyPriceInvalidated(address indexed nftContract, uint256 indexed tokenId); /** * @notice Emitted when a buy price is set by the owner of an NFT. * @dev The NFT is transferred into the market contract for escrow unless it was already escrowed, * e.g. for auction listing. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param seller The address of the NFT owner which set the buy price. * @param price The price of the NFT. */ event BuyPriceSet(address indexed nftContract, uint256 indexed tokenId, address indexed seller, uint256 price); /** * @notice [DEPRECATED] use `buyV2` instead. * Buy the NFT at the set buy price. * `msg.value` must be <= `maxPrice` and any delta will be taken from the account's available FETH balance. * @dev `maxPrice` protects the buyer in case a the price is increased but allows the transaction to continue * when the price is reduced (and any surplus funds provided are refunded). * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param maxPrice The maximum price to pay for the NFT. */ function buy(address nftContract, uint256 tokenId, uint256 maxPrice) external payable { buyV2(nftContract, tokenId, maxPrice, payable(0)); } /** * @notice Buy the NFT at the set buy price. * `msg.value` must be <= `maxPrice` and any delta will be taken from the account's available FETH balance. * @dev `maxPrice` protects the buyer in case a the price is increased but allows the transaction to continue * when the price is reduced (and any surplus funds provided are refunded). * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param maxPrice The maximum price to pay for the NFT. * @param referrer The address of the referrer. */ function buyV2(address nftContract, uint256 tokenId, uint256 maxPrice, address payable referrer) public payable { BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; if (buyPrice.price > maxPrice) { revert NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price(buyPrice.price); } else if (buyPrice.seller == address(0)) { revert NFTMarketBuyPrice_Cannot_Buy_Unset_Price(); } _buy(nftContract, tokenId, referrer); } /** * @notice Removes the buy price set for an NFT. * @dev The NFT is transferred back to the owner unless it's still escrowed for another market tool, * e.g. listed for sale in an auction. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. */ function cancelBuyPrice(address nftContract, uint256 tokenId) external nonReentrant { address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; address sender = _msgSender(); if (seller == address(0)) { // This check is redundant with the next one, but done in order to provide a more clear error message. revert NFTMarketBuyPrice_Cannot_Cancel_Unset_Price(); } else if (seller != sender) { revert NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price(seller); } // Remove the buy price delete nftContractToTokenIdToBuyPrice[nftContract][tokenId]; // Transfer the NFT back to the owner if it is not listed in auction. _transferFromEscrowIfAvailable(nftContract, tokenId, sender); emit BuyPriceCanceled(nftContract, tokenId); } /** * @notice Sets the buy price for an NFT and escrows it in the market contract. * A 0 price is acceptable and valid price you can set, enabling a giveaway to the first collector that calls `buy`. * @dev If there is an offer for this amount or higher, that will be accepted instead of setting a buy price. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param price The price at which someone could buy this NFT. */ function setBuyPrice(address nftContract, uint256 tokenId, uint256 price) external nonReentrant { // If there is a valid offer at this price or higher, accept that instead. if (_autoAcceptOffer(nftContract, tokenId, price)) { return; } if (price > type(uint96).max) { // This ensures that no data is lost when storing the price as `uint96`. revert NFTMarketBuyPrice_Price_Too_High(); } BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; address seller = buyPrice.seller; if (buyPrice.price == price && seller != address(0)) { revert NFTMarketBuyPrice_Price_Already_Set(); } // Store the new price for this NFT. buyPrice.price = uint96(price); address payable sender = payable(_msgSender()); if (seller == address(0)) { // Transfer the NFT into escrow, if it's already in escrow confirm the `msg.sender` is the owner. _transferToEscrow(nftContract, tokenId); // The price was not previously set for this NFT, store the seller. buyPrice.seller = sender; } else if (seller != sender) { // Buy price was previously set by a different user revert NFTMarketBuyPrice_Only_Owner_Can_Set_Price(seller); } emit BuyPriceSet(nftContract, tokenId, sender, price); } /** * @notice If there is a buy price at this price or lower, accept that and return true. */ function _autoAcceptBuyPrice( address nftContract, uint256 tokenId, uint256 maxPrice ) internal override returns (bool) { BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; if (buyPrice.seller == address(0) || buyPrice.price > maxPrice) { // No buy price was found, or the price is too high. return false; } _buy(nftContract, tokenId, payable(0)); return true; } /** * @inheritdoc NFTMarketCore * @dev Invalidates the buy price on a auction start, if one is found. */ function _beforeAuctionStarted(address nftContract, uint256 tokenId) internal virtual override { BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; if (buyPrice.seller != address(0)) { // A buy price was set for this NFT, invalidate it. _invalidateBuyPrice(nftContract, tokenId); } super._beforeAuctionStarted(nftContract, tokenId); } /** * @notice Process the purchase of an NFT at the current buy price. * @dev The caller must confirm that the seller != address(0) before calling this function. */ function _buy(address nftContract, uint256 tokenId, address payable referrer) private nonReentrant { BuyPrice memory buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; // Remove the buy now price delete nftContractToTokenIdToBuyPrice[nftContract][tokenId]; // Cancel the buyer's offer if there is one in order to free up their FETH balance // even if they don't need the FETH for this specific purchase. _cancelSendersOffer(nftContract, tokenId); _tryUseFETHBalance(buyPrice.price, true); address sender = _msgSender(); // Transfer the NFT to the buyer. // The seller was already authorized when the buyPrice was set originally set. _transferFromEscrow(nftContract, tokenId, sender, address(0)); // Distribute revenue for this sale. (uint256 totalFees, uint256 creatorRev, uint256 sellerRev) = _distributeFunds( nftContract, tokenId, buyPrice.seller, buyPrice.price, referrer, payable(0), 0 ); emit BuyPriceAccepted(nftContract, tokenId, buyPrice.seller, sender, totalFees, creatorRev, sellerRev); } /** * @notice Clear a buy price and emit BuyPriceInvalidated. * @dev The caller must confirm the buy price is set before calling this function. */ function _invalidateBuyPrice(address nftContract, uint256 tokenId) private { delete nftContractToTokenIdToBuyPrice[nftContract][tokenId]; emit BuyPriceInvalidated(nftContract, tokenId); } /** * @inheritdoc NFTMarketCore * @dev Invalidates the buy price if one is found before transferring the NFT. * This will revert if there is a buy price set but the `authorizeSeller` is not the owner. */ function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address authorizeSeller ) internal virtual override { address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller != address(0)) { // A buy price was set for this NFT. // `authorizeSeller != address(0) &&` could be added when other mixins use this flow. // ATM that additional check would never return false. if (seller != authorizeSeller) { // When there is a buy price set, the `buyPrice.seller` is the owner of the NFT. revert NFTMarketBuyPrice_Seller_Mismatch(seller); } // The seller authorization has been confirmed. authorizeSeller = address(0); // Invalidate the buy price as the NFT will no longer be in escrow. _invalidateBuyPrice(nftContract, tokenId); } super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller); } /** * @inheritdoc NFTMarketCore * @dev Checks if there is a buy price set, if not then allow the transfer to proceed. */ function _transferFromEscrowIfAvailable( address nftContract, uint256 tokenId, address recipient ) internal virtual override { address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { // A buy price has been set for this NFT so it should remain in escrow. super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient); } } /** * @inheritdoc NFTMarketCore * @dev Checks if the NFT is already in escrow for buy now. */ function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual override { address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { // The NFT is not in escrow for buy now. super._transferToEscrow(nftContract, tokenId); } else if (seller != _msgSender()) { // When there is a buy price set, the `seller` is the owner of the NFT. revert NFTMarketBuyPrice_Seller_Mismatch(seller); } } /** * @notice Returns the buy price details for an NFT if one is available. * @dev If no price is found, seller will be address(0) and price will be max uint256. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return seller The address of the owner that listed a buy price for this NFT. * Returns `address(0)` if there is no buy price set for this NFT. * @return price The price of the NFT. * Returns max uint256 if there is no buy price set for this NFT (since a price of 0 is supported). */ function getBuyPrice(address nftContract, uint256 tokenId) external view returns (address seller, uint256 price) { seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { return (seller, type(uint256).max); } price = nftContractToTokenIdToBuyPrice[nftContract][tokenId].price; } /** * @inheritdoc MarketSharedCore * @dev Returns the seller if there is a buy price set for this NFT, otherwise * bubbles the call up for other considerations. */ function _getSellerOf( address nftContract, uint256 tokenId ) internal view virtual override(MarketSharedCore, NFTMarketCore) returns (address payable seller) { seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { seller = super._getSellerOf(nftContract, tokenId); } } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[1_000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../../interfaces/internal/IFethMarket.sol"; import "../shared/Constants.sol"; import "../shared/MarketSharedCore.sol"; error NFTMarketCore_Seller_Not_Found(); /** * @title A place for common modifiers and functions used by various NFTMarket mixins, if any. * @dev This also leaves a gap which can be used to add a new mixin to the top of the inheritance tree. * @author batu-inal & HardlyDifficult */ abstract contract NFTMarketCore is Initializable, Context, MarketSharedCore { using AddressUpgradeable for address; using AddressUpgradeable for address payable; /** * @notice If there is a buy price at this amount or lower, accept that and return true. */ function _autoAcceptBuyPrice(address nftContract, uint256 tokenId, uint256 amount) internal virtual returns (bool); /** * @notice If there is a valid offer at the given price or higher, accept that and return true. */ function _autoAcceptOffer(address nftContract, uint256 tokenId, uint256 minAmount) internal virtual returns (bool); /** * @notice Notify implementors when an auction has received its first bid. * Once a bid is received the sale is guaranteed to the auction winner * and other sale mechanisms become unavailable. * @dev Implementors of this interface should update internal state to reflect an auction has been kicked off. */ function _beforeAuctionStarted(address /*nftContract*/, uint256 /*tokenId*/) internal virtual { // No-op } /** * @notice Cancel the `msg.sender`'s offer if there is one, freeing up their FETH balance. * @dev This should be used when it does not make sense to keep the original offer around, * e.g. if a collector accepts a Buy Price then keeping the offer around is not necessary. */ function _cancelSendersOffer(address nftContract, uint256 tokenId) internal virtual; /** * @notice Transfers the NFT from escrow and clears any state tracking this escrowed NFT. * @param authorizeSeller The address of the seller pending authorization. * Once it's been authorized by one of the escrow managers, it should be set to address(0) * indicated that it's no longer pending authorization. */ function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address authorizeSeller ) internal virtual { if (authorizeSeller != address(0)) { revert NFTMarketCore_Seller_Not_Found(); } IERC721(nftContract).transferFrom(address(this), recipient, tokenId); } /** * @notice Transfers the NFT from escrow unless there is another reason for it to remain in escrow. */ function _transferFromEscrowIfAvailable(address nftContract, uint256 tokenId, address recipient) internal virtual { _transferFromEscrow(nftContract, tokenId, recipient, address(0)); } /** * @notice Transfers an NFT into escrow, * if already there this requires the msg.sender is authorized to manage the sale of this NFT. */ function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual { IERC721(nftContract).transferFrom(_msgSender(), address(this), tokenId); } /** * @dev Determines the minimum amount when increasing an existing offer or bid. */ function _getMinIncrement(uint256 currentAmount) internal pure returns (uint256) { uint256 minIncrement = currentAmount; unchecked { minIncrement /= MIN_PERCENT_INCREMENT_DENOMINATOR; } if (minIncrement == 0) { // Since minIncrement reduces from the currentAmount, this cannot overflow. // The next amount must be at least 1 wei greater than the current. return currentAmount + 1; } return minIncrement + currentAmount; } /** * @inheritdoc MarketSharedCore */ function _getSellerOf( address nftContract, uint256 tokenId ) internal view virtual override returns (address payable seller) { // No-op by default } /** * @inheritdoc MarketSharedCore */ function _getSellerOrOwnerOf( address nftContract, uint256 tokenId ) internal view override returns (address payable sellerOrOwner) { sellerOrOwner = _getSellerOf(nftContract, tokenId); if (sellerOrOwner == address(0)) { sellerOrOwner = payable(IERC721(nftContract).ownerOf(tokenId)); } } /** * @notice Checks if an escrowed NFT is currently in active auction. * @return Returns false if the auction has ended, even if it has not yet been settled. */ function _isInActiveAuction(address nftContract, uint256 tokenId) internal view virtual returns (bool); /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps * @dev 50 slots were consumed by adding `ReentrancyGuard`. */ uint256[450] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts/utils/Context.sol"; import "../../interfaces/internal/INFTMarketExhibition.sol"; import "../shared/Constants.sol"; /// @param curator The curator for this exhibition. error NFTMarketExhibition_Caller_Is_Not_Curator(address curator); error NFTMarketExhibition_Can_Not_Add_Dupe_Seller(); error NFTMarketExhibition_Curator_Automatically_Allowed(); error NFTMarketExhibition_Exhibition_Does_Not_Exist(); error NFTMarketExhibition_Seller_Not_Allowed_In_Exhibition(); error NFTMarketExhibition_Sellers_Required(); error NFTMarketExhibition_Take_Rate_Too_High(); /** * @title Enables a curation surface for sellers to exhibit their NFTs. * @author HardlyDifficult */ abstract contract NFTMarketExhibition is INFTMarketExhibition, Context { /** * @notice Stores details about an exhibition. */ struct Exhibition { /// @notice The curator which created this exhibition. address payable curator; /// @notice The rate of the sale which goes to the curator. uint16 takeRateInBasisPoints; // 80-bits available in the first slot /// @notice A name for the exhibition. string name; } /// @notice Tracks the next sequence ID to be assigned to an exhibition. uint256 private latestExhibitionId; /// @notice Maps the exhibition ID to their details. mapping(uint256 => Exhibition) private idToExhibition; /// @notice Maps an exhibition to the list of sellers allowed to list with it. mapping(uint256 => mapping(address => bool)) private exhibitionIdToSellerToIsAllowed; /// @notice Maps an NFT to the exhibition it was listed with. mapping(address => mapping(uint256 => uint256)) private nftContractToTokenIdToExhibitionId; /** * @notice Emitted when an exhibition is created. * @param exhibitionId The ID for this exhibition. * @param curator The curator which created this exhibition. * @param name The name for this exhibition. * @param takeRateInBasisPoints The rate of the sale which goes to the curator. */ event ExhibitionCreated( uint256 indexed exhibitionId, address indexed curator, string name, uint16 takeRateInBasisPoints ); /** * @notice Emitted when an exhibition is deleted. * @param exhibitionId The ID for the exhibition. */ event ExhibitionDeleted(uint256 indexed exhibitionId); /** * @notice Emitted when an NFT is listed in an exhibition. * @param nftContract The contract address of the NFT. * @param tokenId The ID of the NFT. * @param exhibitionId The ID of the exhibition it was listed with. */ event NftAddedToExhibition(address indexed nftContract, uint256 indexed tokenId, uint256 indexed exhibitionId); /** * @notice Emitted when an NFT is no longer associated with an exhibition for reasons other than a sale. * @param nftContract The contract address of the NFT. * @param tokenId The ID of the NFT. * @param exhibitionId The ID of the exhibition it was originally listed with. */ event NftRemovedFromExhibition(address indexed nftContract, uint256 indexed tokenId, uint256 indexed exhibitionId); /** * @notice Emitted when sellers are granted access to list with an exhibition. * @param exhibitionId The ID of the exhibition. * @param sellers The list of sellers granted access. */ event SellersAddedToExhibition(uint256 indexed exhibitionId, address[] sellers); /// @notice Requires the caller to be the curator of the exhibition. modifier onlyExhibitionCurator(uint256 exhibitionId) { address curator = idToExhibition[exhibitionId].curator; if (curator != _msgSender()) { if (curator == address(0)) { // If the curator is not a match, check if the exhibition exists in order to provide a better error message. revert NFTMarketExhibition_Exhibition_Does_Not_Exist(); } revert NFTMarketExhibition_Caller_Is_Not_Curator(curator); } _; } /// @notice Requires the caller pass in some number of sellers modifier sellersRequired(address[] calldata sellers) { if (sellers.length == 0) { revert NFTMarketExhibition_Sellers_Required(); } _; } /** * @notice Adds sellers to exhibition. * @param exhibitionId The exhibition ID. * @param sellers The new list of sellers to be allowed to list with this exhibition. */ function addSellersToExhibition( uint256 exhibitionId, address[] calldata sellers ) external onlyExhibitionCurator(exhibitionId) sellersRequired(sellers) { _addSellersToExhibition(exhibitionId, sellers); } /** * @notice Creates an exhibition. * @param name The name for this exhibition. * @param takeRateInBasisPoints The rate of the sale which goes to the msg.sender as the curator of this exhibition. * @param sellers The list of sellers allowed to list with this exhibition. * @dev The list of sellers may be modified after the exhibition is created via addSellersToExhibition, * which only allows for adding (not removing) new sellers. */ function createExhibition( string calldata name, uint16 takeRateInBasisPoints, address[] calldata sellers ) external sellersRequired(sellers) returns (uint256 exhibitionId) { if (takeRateInBasisPoints > MAX_EXHIBITION_TAKE_RATE) { revert NFTMarketExhibition_Take_Rate_Too_High(); } // Create exhibition unchecked { exhibitionId = ++latestExhibitionId; } address payable sender = payable(_msgSender()); idToExhibition[exhibitionId] = Exhibition({ curator: sender, takeRateInBasisPoints: takeRateInBasisPoints, name: name }); emit ExhibitionCreated({ exhibitionId: exhibitionId, curator: sender, name: name, takeRateInBasisPoints: takeRateInBasisPoints }); _addSellersToExhibition(exhibitionId, sellers); } /** * @notice Deletes an exhibition created by the msg.sender. * @param exhibitionId The ID of the exhibition to delete. * @dev Once deleted, any NFTs listed with this exhibition will still be listed but will no longer be associated with * or share revenue with the exhibition. */ function deleteExhibition(uint256 exhibitionId) external onlyExhibitionCurator(exhibitionId) { delete idToExhibition[exhibitionId]; emit ExhibitionDeleted(exhibitionId); } function _addSellersToExhibition(uint256 exhibitionId, address[] calldata sellers) private { // Populate allow list for (uint256 i = 0; i < sellers.length; ) { address seller = sellers[i]; if (exhibitionIdToSellerToIsAllowed[exhibitionId][seller]) { revert NFTMarketExhibition_Can_Not_Add_Dupe_Seller(); } if (seller == _msgSender()) { revert NFTMarketExhibition_Curator_Automatically_Allowed(); } exhibitionIdToSellerToIsAllowed[exhibitionId][seller] = true; unchecked { ++i; } } emit SellersAddedToExhibition(exhibitionId, sellers); } /** * @notice Assigns an NFT to an exhibition. * @param nftContract The contract address of the NFT. * @param tokenId The ID of the NFT. * @param exhibitionId The ID of the exhibition to list the NFT with. * @dev This call is a no-op if the `exhibitionId` is 0. */ function _addNftToExhibition(address nftContract, uint256 tokenId, uint256 exhibitionId) internal { if (exhibitionId != 0) { Exhibition storage exhibition = idToExhibition[exhibitionId]; if (exhibition.curator == address(0)) { revert NFTMarketExhibition_Exhibition_Does_Not_Exist(); } address sender = _msgSender(); if (!exhibitionIdToSellerToIsAllowed[exhibitionId][sender] && exhibition.curator != sender) { revert NFTMarketExhibition_Seller_Not_Allowed_In_Exhibition(); } nftContractToTokenIdToExhibitionId[nftContract][tokenId] = exhibitionId; emit NftAddedToExhibition(nftContract, tokenId, exhibitionId); } } /** * @notice Returns exhibition details if this NFT was assigned to one, and clears the assignment. * @return paymentAddress The address to send the payment to, or address(0) if n/a. * @return takeRateInBasisPoints The rate of the sale which goes to the curator, or 0 if n/a. * @dev This does not emit NftRemovedFromExhibition, instead it's expected that SellerReferralPaid will be emitted. */ function _getExhibitionForPayment( address nftContract, uint256 tokenId ) internal returns (address payable paymentAddress, uint16 takeRateInBasisPoints) { uint256 exhibitionId = nftContractToTokenIdToExhibitionId[nftContract][tokenId]; if (exhibitionId != 0) { paymentAddress = idToExhibition[exhibitionId].curator; takeRateInBasisPoints = idToExhibition[exhibitionId].takeRateInBasisPoints; delete nftContractToTokenIdToExhibitionId[nftContract][tokenId]; } } /** * @notice Clears an NFT's association with an exhibition. */ function _removeNftFromExhibition(address nftContract, uint256 tokenId) internal { uint256 exhibitionId = nftContractToTokenIdToExhibitionId[nftContract][tokenId]; if (exhibitionId != 0) { delete nftContractToTokenIdToExhibitionId[nftContract][tokenId]; emit NftRemovedFromExhibition(nftContract, tokenId, exhibitionId); } } /** * @notice Returns exhibition details for a given ID. * @param exhibitionId The ID of the exhibition to look up. * @return name The name of the exhibition. * @return curator The curator of the exhibition. * @return takeRateInBasisPoints The rate of the sale which goes to the curator. * @dev If the exhibition does not exist or has since been deleted, the curator will be address(0). */ function getExhibition( uint256 exhibitionId ) external view returns (string memory name, address payable curator, uint16 takeRateInBasisPoints) { Exhibition memory exhibition = idToExhibition[exhibitionId]; name = exhibition.name; curator = exhibition.curator; takeRateInBasisPoints = exhibition.takeRateInBasisPoints; } /** * @notice Returns the exhibition ID for a given NFT. * @param nftContract The contract address of the NFT. * @param tokenId The ID of the NFT. * @return exhibitionId The ID of the exhibition this NFT is assigned to, or 0 if it's not assigned to an exhibition. */ function getExhibitionIdForNft(address nftContract, uint256 tokenId) external view returns (uint256 exhibitionId) { exhibitionId = nftContractToTokenIdToExhibitionId[nftContract][tokenId]; } /** * @notice Returns exhibition payment details for a given ID. * @param exhibitionId The ID of the exhibition to look up. * @return curator The curator of the exhibition. * @return takeRateInBasisPoints The rate of the sale which goes to the curator. * @dev If the exhibition does not exist or has since been deleted, the curator will be address(0). */ function getExhibitionPaymentDetails( uint256 exhibitionId ) external view returns (address payable curator, uint16 takeRateInBasisPoints) { Exhibition storage exhibition = idToExhibition[exhibitionId]; curator = exhibition.curator; takeRateInBasisPoints = exhibition.takeRateInBasisPoints; } /** * @notice Checks if a given seller is approved to list with a given exhibition. * @param exhibitionId The ID of the exhibition to check. * @param seller The address of the seller to check. * @return allowedSeller True if the seller is approved to list with the exhibition. */ function isAllowedSellerForExhibition( uint256 exhibitionId, address seller ) external view returns (bool allowedSeller) { address curator = idToExhibition[exhibitionId].curator; if (curator != address(0)) { allowedSeller = exhibitionIdToSellerToIsAllowed[exhibitionId][seller] || seller == curator; } } /** * @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 file uses a total of 500 slots. */ uint256[496] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "../../libraries/TimeLibrary.sol"; import "../shared/MarketFees.sol"; import "../shared/FoundationTreasuryNode.sol"; import "../shared/FETHNode.sol"; import "../shared/SendValueWithFallbackWithdraw.sol"; import "./NFTMarketCore.sol"; error NFTMarketOffer_Cannot_Be_Made_While_In_Auction(); /// @param currentOfferAmount The current highest offer available for this NFT. error NFTMarketOffer_Offer_Below_Min_Amount(uint256 currentOfferAmount); /// @param expiry The time at which the offer had expired. error NFTMarketOffer_Offer_Expired(uint256 expiry); /// @param currentOfferFrom The address of the collector which has made the current highest offer. error NFTMarketOffer_Offer_From_Does_Not_Match(address currentOfferFrom); /// @param minOfferAmount The minimum amount that must be offered in order for it to be accepted. error NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount(uint256 minOfferAmount); /** * @title Allows collectors to make an offer for an NFT, valid for 24-25 hours. * @notice Funds are escrowed in the FETH ERC-20 token contract. * @author batu-inal & HardlyDifficult */ abstract contract NFTMarketOffer is Initializable, FoundationTreasuryNode, Context, FETHNode, NFTMarketCore, ReentrancyGuardUpgradeable, SendValueWithFallbackWithdraw, MarketFees { using AddressUpgradeable for address; using TimeLibrary for uint32; /// @notice Stores offer details for a specific NFT. struct Offer { // Slot 1: When increasing an offer, only this slot is updated. /// @notice The expiration timestamp of when this offer expires. uint32 expiration; /// @notice The amount, in wei, of the highest offer. uint96 amount; /// @notice First slot (of 16B) used for the offerReferrerAddress. // The offerReferrerAddress is the address used to pay the // referrer on an accepted offer. uint128 offerReferrerAddressSlot0; // Slot 2: When the buyer changes, both slots need updating /// @notice The address of the collector who made this offer. address buyer; /// @notice Second slot (of 4B) used for the offerReferrerAddress. uint32 offerReferrerAddressSlot1; // 96 bits (12B) are available in slot 1. } /// @notice Stores the highest offer for each NFT. mapping(address => mapping(uint256 => Offer)) private nftContractToIdToOffer; /** * @notice Emitted when an offer is accepted, * indicating that the NFT has been transferred and revenue from the sale distributed. * @dev The accepted total offer amount is `totalFees` + `creatorRev` + `sellerRev`. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param buyer The address of the collector that made the offer which was accepted. * @param seller The address of the seller which accepted the offer. * @param 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. * @param sellerRev The amount of ETH that was sent to the owner for this sale. */ event OfferAccepted( address indexed nftContract, uint256 indexed tokenId, address indexed buyer, address seller, uint256 totalFees, uint256 creatorRev, uint256 sellerRev ); /** * @notice Emitted when an offer is invalidated due to other market activity. * When this occurs, the collector which made the offer has their FETH balance unlocked * and the funds are available to place other offers or to be withdrawn. * @dev This occurs when the offer is no longer eligible to be accepted, * e.g. when a bid is placed in an auction for this NFT. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. */ event OfferInvalidated(address indexed nftContract, uint256 indexed tokenId); /** * @notice Emitted when an offer is made. * @dev The `amount` of the offer is locked in the FETH ERC-20 contract, guaranteeing that the funds * remain available until the `expiration` date. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param buyer The address of the collector that made the offer to buy this NFT. * @param amount The amount, in wei, of the offer. * @param expiration The expiration timestamp for the offer. */ event OfferMade( address indexed nftContract, uint256 indexed tokenId, address indexed buyer, uint256 amount, uint256 expiration ); /** * @notice Accept the highest offer for an NFT. * @dev The offer must not be expired and the NFT owned + approved by the seller or * available in the market contract's escrow. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param offerFrom The address of the collector that you wish to sell to. * If the current highest offer is not from this user, the transaction will revert. * This could happen if a last minute offer was made by another collector, * and would require the seller to try accepting again. * @param minAmount The minimum value of the highest offer for it to be accepted. * If the value is less than this amount, the transaction will revert. * This could happen if the original offer expires and is replaced with a smaller offer. */ function acceptOffer( address nftContract, uint256 tokenId, address offerFrom, uint256 minAmount ) external nonReentrant { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; // Validate offer expiry and amount if (offer.expiration.hasExpired()) { revert NFTMarketOffer_Offer_Expired(offer.expiration); } else if (offer.amount < minAmount) { revert NFTMarketOffer_Offer_Below_Min_Amount(offer.amount); } // Validate the buyer if (offer.buyer != offerFrom) { revert NFTMarketOffer_Offer_From_Does_Not_Match(offer.buyer); } _acceptOffer(nftContract, tokenId); } /** * @notice Make an offer for any NFT which is valid for 24-25 hours. * The funds will be locked in the FETH token contract and become available once the offer is outbid or has expired. * @dev An offer may be made for an NFT before it is minted, although we generally not recommend you do that. * If there is a buy price set at this price or lower, that will be accepted instead of making an offer. * `msg.value` must be <= `amount` and any delta will be taken from the account's available FETH balance. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param amount The amount to offer for this NFT. * @param referrer The referrer address for the offer. * @return expiration The timestamp for when this offer will expire. * This is provided as a return value in case another contract would like to leverage this information, * user's should refer to the expiration in the `OfferMade` event log. * If the buy price is accepted instead, `0` is returned as the expiration since that's n/a. */ function makeOfferV2( address nftContract, uint256 tokenId, uint256 amount, address payable referrer ) external payable returns (uint256 expiration) { // If there is a buy price set at this price or lower, accept that instead. if (_autoAcceptBuyPrice(nftContract, tokenId, amount)) { // If the buy price is accepted, `0` is returned as the expiration since that's n/a. return 0; } if (_isInActiveAuction(nftContract, tokenId)) { revert NFTMarketOffer_Cannot_Be_Made_While_In_Auction(); } Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; address sender = _msgSender(); if (offer.expiration.hasExpired()) { // This is a new offer for the NFT (no other offer found or the previous offer expired) // Lock the offer amount in FETH until the offer expires in 24-25 hours. expiration = feth.marketLockupFor{ value: msg.value }(sender, amount); } else { // A previous offer exists and has not expired uint256 minIncrement = _getMinIncrement(offer.amount); if (amount < minIncrement) { // A non-trivial increase in price is required to avoid sniping revert NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount(minIncrement); } // Unlock the previous offer so that the FETH tokens are available for other offers or to transfer / withdraw // and lock the new offer amount in FETH until the offer expires in 24-25 hours. expiration = feth.marketChangeLockup{ value: msg.value }( offer.buyer, offer.expiration, offer.amount, sender, amount ); } // Record offer details offer.buyer = sender; // The FETH contract guarantees that the expiration fits into 32 bits. offer.expiration = uint32(expiration); // `amount` is capped by the ETH provided, which cannot realistically overflow 96 bits. offer.amount = uint96(amount); // Set offerReferrerAddressSlot0 to the first 16B of the referrer address. // By shifting the referrer 32 bits to the right we obtain the first 16B. offer.offerReferrerAddressSlot0 = uint128(uint160(address(referrer)) >> 32); // Set offerReferrerAddressSlot1 to the last 4B of the referrer address. // By casting the referrer address to 32bits we discard the first 16B. offer.offerReferrerAddressSlot1 = uint32(uint160(address(referrer))); emit OfferMade(nftContract, tokenId, sender, amount, expiration); } /** * @notice Accept the highest offer for an NFT from the `msg.sender` account. * The NFT will be transferred to the buyer and revenue from the sale will be distributed. * @dev The caller must validate the expiry and amount before calling this helper. * This may invalidate other market tools, such as clearing the buy price if set. */ function _acceptOffer(address nftContract, uint256 tokenId) private { Offer memory offer = nftContractToIdToOffer[nftContract][tokenId]; // Remove offer delete nftContractToIdToOffer[nftContract][tokenId]; // Withdraw ETH from the buyer's account in the FETH token contract. feth.marketWithdrawLocked(offer.buyer, offer.expiration, offer.amount); address payable sender = payable(_msgSender()); // Transfer the NFT to the buyer. try IERC721(nftContract).transferFrom(sender, offer.buyer, tokenId) { // NFT was in the seller's wallet so the transfer is complete. } catch { // If the transfer fails then attempt to transfer from escrow instead. // This should revert if `msg.sender` is not the owner of this NFT. _transferFromEscrow(nftContract, tokenId, offer.buyer, sender); } // Distribute revenue for this sale leveraging the ETH received from the FETH contract in the line above. (uint256 totalFees, uint256 creatorRev, uint256 sellerRev) = _distributeFunds( nftContract, tokenId, sender, offer.amount, _getOfferReferrerFromSlots(offer.offerReferrerAddressSlot0, offer.offerReferrerAddressSlot1), payable(0), 0 ); emit OfferAccepted(nftContract, tokenId, offer.buyer, sender, totalFees, creatorRev, sellerRev); } /** * @inheritdoc NFTMarketCore * @dev Invalidates the highest offer when an auction is kicked off, if one is found. */ function _beforeAuctionStarted(address nftContract, uint256 tokenId) internal virtual override { _invalidateOffer(nftContract, tokenId); super._beforeAuctionStarted(nftContract, tokenId); } /** * @inheritdoc NFTMarketCore */ function _autoAcceptOffer(address nftContract, uint256 tokenId, uint256 minAmount) internal override returns (bool) { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.expiration.hasExpired() || offer.amount < minAmount) { // No offer found, the most recent offer is now expired, or the highest offer is below the minimum amount. return false; } _acceptOffer(nftContract, tokenId); return true; } /** * @inheritdoc NFTMarketCore */ function _cancelSendersOffer(address nftContract, uint256 tokenId) internal override { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.buyer == _msgSender()) { _invalidateOffer(nftContract, tokenId); } } /** * @notice Invalidates the offer and frees ETH from escrow, if the offer has not already expired. * @dev Offers are not invalidated when the NFT is purchased by accepting the buy price unless it * was purchased by the same user. * The user which just purchased the NFT may have buyer's remorse and promptly decide they want a fast exit, * accepting a small loss to limit their exposure. */ function _invalidateOffer(address nftContract, uint256 tokenId) private { if (!nftContractToIdToOffer[nftContract][tokenId].expiration.hasExpired()) { // An offer was found and it has not already expired Offer memory offer = nftContractToIdToOffer[nftContract][tokenId]; // Remove offer delete nftContractToIdToOffer[nftContract][tokenId]; // Unlock the offer so that the FETH tokens are available for other offers or to transfer / withdraw feth.marketUnlockFor(offer.buyer, offer.expiration, offer.amount); emit OfferInvalidated(nftContract, tokenId); } } /** * @notice Returns the minimum amount a collector must offer for this NFT in order for the offer to be valid. * @dev Offers for this NFT which are less than this value will revert. * Once the previous offer has expired smaller offers can be made. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return minimum The minimum amount that must be offered for this NFT. */ function getMinOfferAmount(address nftContract, uint256 tokenId) external view returns (uint256 minimum) { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (!offer.expiration.hasExpired()) { return _getMinIncrement(offer.amount); } // Absolute min is anything > 0 return 1; } /** * @notice Returns details about the current highest offer for an NFT. * @dev Default values are returned if there is no offer or the offer has expired. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return buyer The address of the buyer that made the current highest offer. * Returns `address(0)` if there is no offer or the most recent offer has expired. * @return expiration The timestamp that the current highest offer expires. * Returns `0` if there is no offer or the most recent offer has expired. * @return amount The amount being offered for this NFT. * Returns `0` if there is no offer or the most recent offer has expired. */ function getOffer( address nftContract, uint256 tokenId ) external view returns (address buyer, uint256 expiration, uint256 amount) { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.expiration.hasExpired()) { // Offer not found or has expired return (address(0), 0, 0); } // An offer was found and it has not yet expired. return (offer.buyer, offer.expiration, offer.amount); } /** * @notice Returns the current highest offer's referral for an NFT. * @dev Default value of `payable(0)` is returned if * there is no offer, the offer has expired or does not have a referral. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return referrer The payable address of the referrer for the offer. */ function getOfferReferrer(address nftContract, uint256 tokenId) external view returns (address payable referrer) { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.expiration.hasExpired()) { // Offer not found or has expired return payable(0); } return _getOfferReferrerFromSlots(offer.offerReferrerAddressSlot0, offer.offerReferrerAddressSlot1); } function _getOfferReferrerFromSlots( uint128 offerReferrerAddressSlot0, uint32 offerReferrerAddressSlot1 ) private pure returns (address payable referrer) { // Stitch offerReferrerAddressSlot0 and offerReferrerAddressSlot1 to obtain the payable offerReferrerAddress. // Left shift offerReferrerAddressSlot0 by 32 bits OR it with offerReferrerAddressSlot1. referrer = payable(address((uint160(offerReferrerAddressSlot0) << 32) | uint160(offerReferrerAddressSlot1))); } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[1_000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; /** * @title Reserves space previously occupied by private sales. * @author batu-inal & HardlyDifficult */ abstract contract NFTMarketPrivateSaleGap { // Original data: // bytes32 private __gap_was_DOMAIN_SEPARATOR; // mapping(address => mapping(uint256 => mapping(address => mapping(address => mapping(uint256 => // mapping(uint256 => bool)))))) private privateSaleInvalidated; // uint256[999] private __gap; /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps * @dev 1 slot was consumed by privateSaleInvalidated. */ uint256[1001] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "../../interfaces/internal/routes/INFTMarketReserveAuction.sol"; import "../../libraries/TimeLibrary.sol"; import "../shared/FoundationTreasuryNode.sol"; import "../shared/FETHNode.sol"; import "../shared/MarketFees.sol"; import "../shared/MarketSharedCore.sol"; import "../shared/SendValueWithFallbackWithdraw.sol"; import "./NFTMarketAuction.sol"; import "./NFTMarketCore.sol"; import "./NFTMarketExhibition.sol"; /// @param auctionId The already listed auctionId for this NFT. error NFTMarketReserveAuction_Already_Listed(uint256 auctionId); /// @param minAmount The minimum amount that must be bid in order for it to be accepted. error NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount(uint256 minAmount); /// @param reservePrice The current reserve price. error NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price(uint256 reservePrice); /// @param endTime The timestamp at which the auction had ended. error NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction(uint256 endTime); error NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction(); error NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction(); /// @param endTime The timestamp at which the auction will end. error NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress(uint256 endTime); error NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid(); error NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress(); /// @param maxDuration The maximum configuration for a duration of the auction, in seconds. error NFTMarketReserveAuction_Exceeds_Max_Duration(uint256 maxDuration); /// @param extensionDuration The extension duration, in seconds. error NFTMarketReserveAuction_Less_Than_Extension_Duration(uint256 extensionDuration); error NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price(); /// @param seller The current owner of the NFT. error NFTMarketReserveAuction_Not_Matching_Seller(address seller); /// @param owner The current owner of the NFT. error NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(address owner); error NFTMarketReserveAuction_Price_Already_Set(); error NFTMarketReserveAuction_Too_Much_Value_Provided(); /** * @title Allows the owner of an NFT to list it in auction. * @notice NFTs in auction are escrowed in the market contract. * @dev There is room to optimize the storage for auctions, significantly reducing gas costs. * This may be done in the future, but for now it will remain as is in order to ease upgrade compatibility. * @author batu-inal & HardlyDifficult */ abstract contract NFTMarketReserveAuction is INFTMarketReserveAuction, Initializable, FoundationTreasuryNode, Context, FETHNode, MarketSharedCore, NFTMarketCore, ReentrancyGuardUpgradeable, SendValueWithFallbackWithdraw, MarketFees, NFTMarketExhibition, NFTMarketAuction { using TimeLibrary for uint256; /// @notice The auction configuration for a specific NFT. struct ReserveAuction { /// @notice The address of the NFT contract. address nftContract; /// @notice The id of the NFT. uint256 tokenId; /// @notice The owner of the NFT which listed it in auction. address payable seller; /// @notice The duration for this auction. uint256 duration; /// @notice The extension window for this auction. uint256 extensionDuration; /// @notice The time at which this auction will not accept any new bids. /// @dev This is `0` until the first bid is placed. uint256 endTime; /// @notice The current highest bidder in this auction. /// @dev This is `address(0)` until the first bid is placed. address payable bidder; /// @notice The latest price of the NFT in this auction. /// @dev This is set to the reserve price, and then to the highest bid once the auction has started. uint256 amount; } /// @notice Stores the auction configuration for a specific NFT. /// @dev This allows us to modify the storage struct without changing external APIs. struct ReserveAuctionStorage { /// @notice The address of the NFT contract. address nftContract; /// @notice The id of the NFT. uint256 tokenId; /// @notice The owner of the NFT which listed it in auction. address payable seller; /// @notice First slot (of 12B) used for the bidReferrerAddress. /// The bidReferrerAddress is the address used to pay the referrer on finalize. /// @dev This approach is used in order to pack storage, saving gas. uint96 bidReferrerAddressSlot0; /// @dev This field is no longer used. uint256 __gap_was_duration; /// @dev This field is no longer used. uint256 __gap_was_extensionDuration; /// @notice The time at which this auction will not accept any new bids. /// @dev This is `0` until the first bid is placed. uint256 endTime; /// @notice The current highest bidder in this auction. /// @dev This is `address(0)` until the first bid is placed. address payable bidder; /// @notice Second slot (of 8B) used for the bidReferrerAddress. uint64 bidReferrerAddressSlot1; /// @notice The latest price of the NFT in this auction. /// @dev This is set to the reserve price, and then to the highest bid once the auction has started. uint256 amount; } /// @notice The auction configuration for a specific auction id. mapping(address => mapping(uint256 => uint256)) private nftContractToTokenIdToAuctionId; /// @notice The auction id for a specific NFT. /// @dev This is deleted when an auction is finalized or canceled. mapping(uint256 => ReserveAuctionStorage) private auctionIdToAuction; /** * @dev Removing old unused variables in an upgrade safe way. Was: * uint256 private __gap_was_minPercentIncrementInBasisPoints; * uint256 private __gap_was_maxBidIncrementRequirement; * uint256 private __gap_was_duration; * uint256 private __gap_was_extensionDuration; * uint256 private __gap_was_goLiveDate; */ uint256[5] private __gap_was_config; /// @notice How long an auction lasts for once the first bid has been received. uint256 private immutable DURATION; /// @notice The window for auction extensions, any bid placed in the final 15 minutes /// of an auction will reset the time remaining to 15 minutes. uint256 private constant EXTENSION_DURATION = 15 minutes; /// @notice Caps the max duration that may be configured so that overflows will not occur. uint256 private constant MAX_MAX_DURATION = 1_000 days; /** * @notice Emitted when a bid is placed. * @param auctionId The id of the auction this bid was for. * @param bidder The address of the bidder. * @param amount The amount of the bid. * @param endTime The new end time of the auction (which may have been set or extended by this bid). */ event ReserveAuctionBidPlaced(uint256 indexed auctionId, address indexed bidder, uint256 amount, uint256 endTime); /** * @notice Emitted when an auction is canceled. * @dev This is only possible if the auction has not received any bids. * @param auctionId The id of the auction that was canceled. */ event ReserveAuctionCanceled(uint256 indexed auctionId); /** * @notice Emitted when an NFT is listed for auction. * @param seller The address of the seller. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param duration The duration of the auction (always 24-hours). * @param extensionDuration The duration of the auction extension window (always 15-minutes). * @param reservePrice The reserve price to kick off the auction. * @param auctionId The id of the auction that was created. */ event ReserveAuctionCreated( address indexed seller, address indexed nftContract, uint256 indexed tokenId, uint256 duration, uint256 extensionDuration, uint256 reservePrice, uint256 auctionId ); /** * @notice Emitted when an auction that has already ended is finalized, * indicating that the NFT has been transferred and revenue from the sale distributed. * @dev The amount of the highest bid / final sale price for this auction * is `totalFees` + `creatorRev` + `sellerRev`. * @param auctionId The id of the auction that was finalized. * @param seller The address of the seller. * @param bidder The address of the highest bidder that won the NFT. * @param 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. * @param sellerRev The amount of ETH that was sent to the owner for this sale. */ event ReserveAuctionFinalized( uint256 indexed auctionId, address indexed seller, address indexed bidder, uint256 totalFees, uint256 creatorRev, uint256 sellerRev ); /** * @notice Emitted when an auction is invalidated due to other market activity. * @dev This occurs when the NFT is sold another way, such as with `buy` or `acceptOffer`. * @param auctionId The id of the auction that was invalidated. */ event ReserveAuctionInvalidated(uint256 indexed auctionId); /** * @notice Emitted when the auction's reserve price is changed. * @dev This is only possible if the auction has not received any bids. * @param auctionId The id of the auction that was updated. * @param reservePrice The new reserve price for the auction. */ event ReserveAuctionUpdated(uint256 indexed auctionId, uint256 reservePrice); /// @notice Confirms that the reserve price is not zero. modifier onlyValidAuctionConfig(uint256 reservePrice) { if (reservePrice == 0) { revert NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price(); } _; } /** * @notice Configures the duration for auctions. * @param duration The duration for auctions, in seconds. */ constructor(uint256 duration) { if (duration > MAX_MAX_DURATION) { // This ensures that math in this file will not overflow due to a huge duration. revert NFTMarketReserveAuction_Exceeds_Max_Duration(MAX_MAX_DURATION); } if (duration < EXTENSION_DURATION) { // The auction duration configuration must be greater than the extension window of 15 minutes revert NFTMarketReserveAuction_Less_Than_Extension_Duration(EXTENSION_DURATION); } DURATION = duration; } /** * @notice If an auction has been created but has not yet received bids, it may be canceled by the seller. * @dev The NFT is transferred back to the owner unless there is still a buy price set. * @param auctionId The id of the auction to cancel. */ function cancelReserveAuction(uint256 auctionId) external nonReentrant { ReserveAuctionStorage memory auction = auctionIdToAuction[auctionId]; if (auction.seller != _msgSender()) { revert NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(auction.seller); } if (auction.endTime != 0) { revert NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress(); } // Remove the auction. delete nftContractToTokenIdToAuctionId[auction.nftContract][auction.tokenId]; delete auctionIdToAuction[auctionId]; _removeNftFromExhibition(auction.nftContract, auction.tokenId); // Transfer the NFT unless it still has a buy price set. _transferFromEscrowIfAvailable(auction.nftContract, auction.tokenId, auction.seller); emit ReserveAuctionCanceled(auctionId); } /** * @notice [DEPRECATED] use `createReserveAuctionV2` instead. * Creates an auction for the given NFT. * The NFT is held in escrow until the auction is finalized or canceled. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param reservePrice The initial reserve price for the auction. */ function createReserveAuction(address nftContract, uint256 tokenId, uint256 reservePrice) external { createReserveAuctionV2({ nftContract: nftContract, tokenId: tokenId, reservePrice: reservePrice, exhibitionId: 0 }); } /** * @notice Creates an auction for the given NFT. * The NFT is held in escrow until the auction is finalized or canceled. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param reservePrice The initial reserve price for the auction. * @param exhibitionId The exhibition to list with, or 0 if n/a. * @return auctionId The id of the auction that was created. */ function createReserveAuctionV2( address nftContract, uint256 tokenId, uint256 reservePrice, uint256 exhibitionId ) public nonReentrant onlyValidAuctionConfig(reservePrice) returns (uint256 auctionId) { auctionId = _getNextAndIncrementAuctionId(); // If the `msg.sender` is not the owner of the NFT, transferring into escrow should fail. _transferToEscrow(nftContract, tokenId); // This check must be after _transferToEscrow in case auto-settle was required if (nftContractToTokenIdToAuctionId[nftContract][tokenId] != 0) { revert NFTMarketReserveAuction_Already_Listed(nftContractToTokenIdToAuctionId[nftContract][tokenId]); } // Store the auction details address payable sender = payable(_msgSender()); nftContractToTokenIdToAuctionId[nftContract][tokenId] = auctionId; ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; auction.nftContract = nftContract; auction.tokenId = tokenId; auction.seller = sender; auction.amount = reservePrice; _addNftToExhibition(nftContract, tokenId, exhibitionId); emit ReserveAuctionCreated({ seller: sender, nftContract: nftContract, tokenId: tokenId, duration: DURATION, extensionDuration: EXTENSION_DURATION, reservePrice: reservePrice, auctionId: auctionId }); } /** * @notice Once the countdown has expired for an auction, anyone can settle the auction. * This will send the NFT to the highest bidder and distribute revenue for this sale. * @param auctionId The id of the auction to settle. */ function finalizeReserveAuction(uint256 auctionId) external nonReentrant { if (auctionIdToAuction[auctionId].endTime == 0) { revert NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction(); } _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: false }); } /** * @notice [DEPRECATED] use `placeBidV2` instead. * Place a bid in an auction. * A bidder may place a bid which is at least the value defined by `getMinBidAmount`. * If this is the first bid on the auction, the countdown will begin. * If there is already an outstanding bid, the previous bidder will be refunded at this time * and if the bid is placed in the final moments of the auction, the countdown may be extended. * @param auctionId The id of the auction to bid on. */ function placeBid(uint256 auctionId) external payable { placeBidV2({ auctionId: auctionId, amount: msg.value, referrer: payable(0) }); } /** * @notice Place a bid in an auction. * A bidder may place a bid which is at least the amount defined by `getMinBidAmount`. * If this is the first bid on the auction, the countdown will begin. * If there is already an outstanding bid, the previous bidder will be refunded at this time * and if the bid is placed in the final moments of the auction, the countdown may be extended. * @dev `amount` - `msg.value` is withdrawn from the bidder's FETH balance. * @param auctionId The id of the auction to bid on. * @param amount The amount to bid, if this is more than `msg.value` funds will be withdrawn from your FETH balance. * @param referrer The address of the referrer of this bid, or 0 if n/a. */ function placeBidV2(uint256 auctionId, uint256 amount, address payable referrer) public payable nonReentrant { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.amount == 0) { // No auction found revert NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction(); } else if (amount < msg.value) { // The amount is specified by the bidder, so if too much ETH is sent then something went wrong. revert NFTMarketReserveAuction_Too_Much_Value_Provided(); } uint256 endTime = auction.endTime; address payable sender = payable(_msgSender()); // Store the bid referral if (referrer != address(0) || endTime != 0) { auction.bidReferrerAddressSlot0 = uint96(uint160(address(referrer)) >> 64); auction.bidReferrerAddressSlot1 = uint64(uint160(address(referrer))); } if (endTime == 0) { // This is the first bid, kicking off the auction. if (amount < auction.amount) { // The bid must be >= the reserve price. revert NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price(auction.amount); } // Notify other market tools that an auction for this NFT has been kicked off. // The only state change before this call is potentially withdrawing funds from FETH. _beforeAuctionStarted(auction.nftContract, auction.tokenId); // Store the bid details. auction.amount = amount; auction.bidder = sender; // On the first bid, set the endTime to now + duration. unchecked { // Duration is always set to 24hrs so the below can't overflow. endTime = block.timestamp + DURATION; } auction.endTime = endTime; } else { if (endTime.hasExpired()) { // The auction has already ended. revert NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction(endTime); } else if (auction.bidder == sender) { // We currently do not allow a bidder to increase their bid unless another user has outbid them first. revert NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid(); } else { uint256 minIncrement = _getMinIncrement(auction.amount); if (amount < minIncrement) { // If this bid outbids another, it must be at least 10% greater than the last bid. revert NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount(minIncrement); } } // Cache and update bidder state uint256 originalAmount = auction.amount; address payable originalBidder = auction.bidder; auction.amount = amount; auction.bidder = sender; unchecked { // When a bid outbids another, check to see if a time extension should apply. // We confirmed that the auction has not ended, so endTime is always >= the current timestamp. // Current time plus extension duration (always 15 mins) cannot overflow. uint256 endTimeWithExtension = block.timestamp + EXTENSION_DURATION; if (endTime < endTimeWithExtension) { endTime = endTimeWithExtension; auction.endTime = endTime; } } // Refund the previous bidder _sendValueWithFallbackWithdraw({ user: originalBidder, amount: originalAmount, gasLimit: SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT }); } _tryUseFETHBalance({ totalAmount: amount, shouldRefundSurplus: false }); emit ReserveAuctionBidPlaced({ auctionId: auctionId, bidder: sender, amount: amount, endTime: endTime }); } /** * @notice If an auction has been created but has not yet received bids, the reservePrice may be * changed by the seller. * @param auctionId The id of the auction to change. * @param reservePrice The new reserve price for this auction. */ function updateReserveAuction(uint256 auctionId, uint256 reservePrice) external onlyValidAuctionConfig(reservePrice) { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.seller != _msgSender()) { revert NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(auction.seller); } else if (auction.endTime != 0) { revert NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress(); } else if (auction.amount == reservePrice) { revert NFTMarketReserveAuction_Price_Already_Set(); } // Update the current reserve price. auction.amount = reservePrice; emit ReserveAuctionUpdated(auctionId, reservePrice); } /** * @notice Settle an auction that has already ended. * This will send the NFT to the highest bidder and distribute revenue for this sale. * @param keepInEscrow If true, the NFT will be kept in escrow to save gas by avoiding * redundant transfers if the NFT should remain in escrow, such as when the new owner * sets a buy price or lists it in a new auction. */ function _finalizeReserveAuction(uint256 auctionId, bool keepInEscrow) private { ReserveAuctionStorage memory auction = auctionIdToAuction[auctionId]; if (!auction.endTime.hasExpired()) { revert NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress(auction.endTime); } ( address payable sellerReferrerPaymentAddress, uint16 sellerReferrerTakeRateInBasisPoints ) = _getExhibitionForPayment(auction.nftContract, auction.tokenId); // Remove the auction. delete nftContractToTokenIdToAuctionId[auction.nftContract][auction.tokenId]; delete auctionIdToAuction[auctionId]; if (!keepInEscrow) { // The seller was authorized when the auction was originally created super._transferFromEscrow({ nftContract: auction.nftContract, tokenId: auction.tokenId, recipient: auction.bidder, authorizeSeller: address(0) }); } // Distribute revenue for this sale. (uint256 totalFees, uint256 creatorRev, uint256 sellerRev) = _distributeFunds( auction.nftContract, auction.tokenId, auction.seller, auction.amount, payable(address((uint160(auction.bidReferrerAddressSlot0) << 64) | uint160(auction.bidReferrerAddressSlot1))), sellerReferrerPaymentAddress, sellerReferrerTakeRateInBasisPoints ); emit ReserveAuctionFinalized(auctionId, auction.seller, auction.bidder, totalFees, creatorRev, sellerRev); } /** * @inheritdoc NFTMarketCore * @dev If an auction is found: * - If the auction is over, it will settle the auction and confirm the new seller won the auction. * - If the auction has not received a bid, it will invalidate the auction. * - If the auction is in progress, this will revert. */ function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address authorizeSeller ) internal virtual override { uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; if (auctionId != 0) { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.endTime == 0) { // The auction has not received any bids yet so it may be invalided. if (authorizeSeller != address(0) && auction.seller != authorizeSeller) { // The account trying to transfer the NFT is not the current owner. revert NFTMarketReserveAuction_Not_Matching_Seller(auction.seller); } // Remove the auction. delete nftContractToTokenIdToAuctionId[nftContract][tokenId]; delete auctionIdToAuction[auctionId]; _removeNftFromExhibition(nftContract, tokenId); emit ReserveAuctionInvalidated(auctionId); } else { // If the auction has ended, the highest bidder will be the new owner // and if the auction is in progress, this will revert. // `authorizeSeller != address(0)` does not apply here since an unsettled auction must go // through this path to know who the authorized seller should be. if (auction.bidder != authorizeSeller) { revert NFTMarketReserveAuction_Not_Matching_Seller(auction.bidder); } // Finalization will revert if the auction has not yet ended. _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: true }); } // The seller authorization has been confirmed. authorizeSeller = address(0); } super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller); } /** * @inheritdoc NFTMarketCore * @dev Checks if there is an auction for this NFT before allowing the transfer to continue. */ function _transferFromEscrowIfAvailable( address nftContract, uint256 tokenId, address recipient ) internal virtual override { if (nftContractToTokenIdToAuctionId[nftContract][tokenId] == 0) { // No auction was found super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient); } } /** * @inheritdoc NFTMarketCore */ function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual override { uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; if (auctionId == 0) { // NFT is not in auction super._transferToEscrow(nftContract, tokenId); return; } // Using storage saves gas since most of the data is not needed ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; address sender = _msgSender(); if (auction.endTime == 0) { // Reserve price set, confirm the seller is a match if (auction.seller != sender) { revert NFTMarketReserveAuction_Not_Matching_Seller(auction.seller); } } else { // Auction in progress, confirm the highest bidder is a match if (auction.bidder != sender) { revert NFTMarketReserveAuction_Not_Matching_Seller(auction.bidder); } // Finalize auction but leave NFT in escrow, reverts if the auction has not ended _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: true }); } } /** * @notice Returns the minimum amount a bidder must spend to participate in an auction. * Bids must be greater than or equal to this value or they will revert. * @param auctionId The id of the auction to check. * @return minimum The minimum amount for a bid to be accepted. */ function getMinBidAmount(uint256 auctionId) external view returns (uint256 minimum) { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.endTime == 0) { return auction.amount; } return _getMinIncrement(auction.amount); } /** * @notice Returns auction details for a given auctionId. * @param auctionId The id of the auction to lookup. */ function getReserveAuction(uint256 auctionId) external view returns (ReserveAuction memory auction) { ReserveAuctionStorage storage auctionStorage = auctionIdToAuction[auctionId]; auction = ReserveAuction( auctionStorage.nftContract, auctionStorage.tokenId, auctionStorage.seller, DURATION, EXTENSION_DURATION, auctionStorage.endTime, auctionStorage.bidder, auctionStorage.amount ); } /** * @notice Returns the auctionId for a given NFT, or 0 if no auction is found. * @dev If an auction is canceled, it will not be returned. However the auction may be over and pending finalization. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return auctionId The id of the auction, or 0 if no auction is found. */ function getReserveAuctionIdFor(address nftContract, uint256 tokenId) external view returns (uint256 auctionId) { auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; } /** * @notice Returns the referrer for the current highest bid in the auction, or address(0). */ function getReserveAuctionBidReferrer(uint256 auctionId) external view returns (address payable referrer) { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; referrer = payable( address((uint160(auction.bidReferrerAddressSlot0) << 64) | uint160(auction.bidReferrerAddressSlot1)) ); } /** * @inheritdoc MarketSharedCore * @dev Returns the seller that has the given NFT in escrow for an auction, * or bubbles the call up for other considerations. */ function _getSellerOf( address nftContract, uint256 tokenId ) internal view virtual override(MarketSharedCore, NFTMarketCore) returns (address payable seller) { seller = auctionIdToAuction[nftContractToTokenIdToAuctionId[nftContract][tokenId]].seller; if (seller == address(0)) { seller = super._getSellerOf(nftContract, tokenId); } } /** * @inheritdoc NFTMarketCore */ function _isInActiveAuction(address nftContract, uint256 tokenId) internal view override returns (bool) { uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; return auctionId != 0 && !auctionIdToAuction[auctionId].endTime.hasExpired(); } /** * @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.12; /// 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;
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.12; 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.12; 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.12; 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.12; 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); /** * @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.12; 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.12; 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; }
{ "optimizer": { "enabled": true, "runs": 10000 }, "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":"uint256","name":"duration","type":"uint256"},{"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":[{"internalType":"uint256","name":"buyPrice","type":"uint256"}],"name":"NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Cannot_Buy_Unset_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Cannot_Cancel_Unset_Price","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketBuyPrice_Only_Owner_Can_Set_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Price_Already_Set","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Price_Too_High","type":"error"},{"inputs":[{"internalType":"address","name":"seller","type":"address"}],"name":"NFTMarketBuyPrice_Seller_Mismatch","type":"error"},{"inputs":[],"name":"NFTMarketCore_Seller_Not_Found","type":"error"},{"inputs":[{"internalType":"address","name":"curator","type":"address"}],"name":"NFTMarketExhibition_Caller_Is_Not_Curator","type":"error"},{"inputs":[],"name":"NFTMarketExhibition_Can_Not_Add_Dupe_Seller","type":"error"},{"inputs":[],"name":"NFTMarketExhibition_Curator_Automatically_Allowed","type":"error"},{"inputs":[],"name":"NFTMarketExhibition_Exhibition_Does_Not_Exist","type":"error"},{"inputs":[],"name":"NFTMarketExhibition_Seller_Not_Allowed_In_Exhibition","type":"error"},{"inputs":[],"name":"NFTMarketExhibition_Sellers_Required","type":"error"},{"inputs":[],"name":"NFTMarketExhibition_Take_Rate_Too_High","type":"error"},{"inputs":[],"name":"NFTMarketFees_Invalid_Protocol_Fee","type":"error"},{"inputs":[],"name":"NFTMarketFees_Royalty_Registry_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTMarketOffer_Cannot_Be_Made_While_In_Auction","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentOfferAmount","type":"uint256"}],"name":"NFTMarketOffer_Offer_Below_Min_Amount","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"NFTMarketOffer_Offer_Expired","type":"error"},{"inputs":[{"internalType":"address","name":"currentOfferFrom","type":"address"}],"name":"NFTMarketOffer_Offer_From_Does_Not_Match","type":"error"},{"inputs":[{"internalType":"uint256","name":"minOfferAmount","type":"uint256"}],"name":"NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount","type":"error"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"NFTMarketReserveAuction_Already_Listed","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount","type":"error"},{"inputs":[{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price","type":"error"},{"inputs":[{"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction","type":"error"},{"inputs":[{"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxDuration","type":"uint256"}],"name":"NFTMarketReserveAuction_Exceeds_Max_Duration","type":"error"},{"inputs":[{"internalType":"uint256","name":"extensionDuration","type":"uint256"}],"name":"NFTMarketReserveAuction_Less_Than_Extension_Duration","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price","type":"error"},{"inputs":[{"internalType":"address","name":"seller","type":"address"}],"name":"NFTMarketReserveAuction_Not_Matching_Seller","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketReserveAuction_Only_Owner_Can_Update_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Price_Already_Set","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Too_Much_Value_Provided","type":"error"},{"inputs":[],"name":"RouterContext_Not_A_Contract","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorRev","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"BuyPriceAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BuyPriceCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BuyPriceInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"BuyPriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyReferrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyReferrerFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"buyReferrerSellerFee","type":"uint256"}],"name":"BuyReferralPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"exhibitionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"curator","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"uint16","name":"takeRateInBasisPoints","type":"uint16"}],"name":"ExhibitionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"name":"ExhibitionDeleted","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":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"name":"NftAddedToExhibition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"name":"NftRemovedFromExhibition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorRev","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"OfferAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"OfferInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiration","type":"uint256"}],"name":"OfferMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"bidder","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"ReserveAuctionBidPlaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"extensionDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reservePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"address","name":"bidder","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorRev","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"ReserveAuctionFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"ReserveAuctionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"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":"uint256","name":"exhibitionId","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"sellers","type":"address[]"}],"name":"SellersAddedToExhibition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawalToFETH","type":"event"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"offerFrom","type":"address"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"acceptOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"exhibitionId","type":"uint256"},{"internalType":"address[]","name":"sellers","type":"address[]"}],"name":"addSellersToExhibition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"buyV2","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"cancelBuyPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"cancelReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint16","name":"takeRateInBasisPoints","type":"uint16"},{"internalType":"address[]","name":"sellers","type":"address[]"}],"name":"createExhibition","outputs":[{"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"createReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"},{"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"name":"createReserveAuctionV2","outputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"name":"deleteExhibition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"finalizeReserveAuction","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":"uint256","name":"tokenId","type":"uint256"}],"name":"getBuyPrice","outputs":[{"internalType":"address","name":"seller","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"name":"getExhibition","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address payable","name":"curator","type":"address"},{"internalType":"uint16","name":"takeRateInBasisPoints","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getExhibitionIdForNft","outputs":[{"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"exhibitionId","type":"uint256"}],"name":"getExhibitionPaymentDetails","outputs":[{"internalType":"address payable","name":"curator","type":"address"},{"internalType":"uint16","name":"takeRateInBasisPoints","type":"uint16"}],"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":[],"name":"getFoundationTreasury","outputs":[{"internalType":"address payable","name":"treasuryAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getMinBidAmount","outputs":[{"internalType":"uint256","name":"minimum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getMinOfferAmount","outputs":[{"internalType":"uint256","name":"minimum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getOffer","outputs":[{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getOfferReferrer","outputs":[{"internalType":"address payable","name":"referrer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getReserveAuction","outputs":[{"components":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address payable","name":"seller","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"extensionDuration","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"address payable","name":"bidder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct NFTMarketReserveAuction.ReserveAuction","name":"auction","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getReserveAuctionBidReferrer","outputs":[{"internalType":"address payable","name":"referrer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getReserveAuctionIdFor","outputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyRegistry","outputs":[{"internalType":"address","name":"registry","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"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":"uint256","name":"exhibitionId","type":"uint256"},{"internalType":"address","name":"seller","type":"address"}],"name":"isAllowedSellerForExhibition","outputs":[{"internalType":"bool","name":"allowedSeller","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"makeOfferV2","outputs":[{"internalType":"uint256","name":"expiration","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"placeBid","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"placeBidV2","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"setBuyPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"updateReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6101806040523480156200001257600080fd5b50604051620062c8380380620062c88339810160408190526200003591620002f3565b816101f484600087858a6001600160a01b0381163b620000685760405163028bba2560e61b815260040160405180910390fd5b6001600160a01b0390811660805281163b6200009757604051633d7a0d8f60e11b815260040160405180910390fd5b6001600160a01b0390811660a05281163b620000c65760405163de58082760e01b815260040160405180910390fd5b6001600160a01b031660c052620000e160646127106200037d565b620000ef906127106200037d565b8361ffff1610806200013d57506200010c611388612710620003a0565b6200011c6103e86127106200037d565b6200012a906127106200037d565b6200013a9061ffff8616620003bc565b10155b156200015c57604051630567777b60e41b815260040160405180910390fd5b61ffff831660e0526001600160a01b0382163b6200018d5760405163dd78160760e01b815260040160405180910390fd5b6001600160a01b03909116610100521515610140525030610120526305265c00811115620001d95760405163ccd285bd60e01b81526305265c0060048201526024015b60405180910390fd5b610384811015620002025760405163494c8c0760e11b81526103846004820152602401620001d0565b61016052620002106200021b565b5050505050620003d2565b600054610100900460ff1615620002855760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b6064820152608401620001d0565b60005460ff9081161015620002d8576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b0381168114620002f057600080fd5b50565b600080600080600060a086880312156200030c57600080fd5b85516200031981620002da565b60208701519095506200032c81620002da565b60408701519094506200033f81620002da565b6060870151608088015191945092506200035981620002da565b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b6000826200039b57634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115620003b657620003b662000367565b92915050565b80820180821115620003b657620003b662000367565b60805160a05160c05160e05161010051610120516101405161016051615e27620004a1600039600081816121cc015281816125cf0152612925015260008181613d3b0152613d64015260008181613b4f01528181613bf90152613cbc0152600081816109190152612a4a01526000613adb015260008181610299015281816107090152818161182c0152818161197b015281816132cf0152818161431e015281816143c10152614fe501526000818161066101526145e201526000818161098c01526147570152615e276000f3fe6080604052600436106102895760003560e01c80636a90a82711610153578063a59ac6dd116100cb578063beb5127c1161007f578063e5d1e72311610064578063e5d1e7231461093d578063efef76f81461095d578063f7a2da231461097d57600080fd5b8063beb5127c146108ea578063daa351d41461090a57600080fd5b8063af1e1de3116100b0578063af1e1de314610892578063b01ef608146108c4578063b6aff8c1146108d757600080fd5b8063a59ac6dd1461083a578063ac71045e1461084d57600080fd5b80638129fc1c116101225780639979ef45116101075780639979ef451461072d5780639e64ba6c146107405780639e79b41f146107aa57600080fd5b80638129fc1c146106e5578063895633ba146106fa57600080fd5b80636a90a827146106525780637430e0c614610685578063798bac8d146106a55780637b3a5884146106c557600080fd5b8063442559a2116102015780634ce6931a116101b557806355daed3e1161019a57806355daed3e146105b8578063614b151c1461061f5780636512ed2d1461063257600080fd5b80634ce6931a146105785780634fca06c61461059857600080fd5b80634635256e116101e65780634635256e146104f957806347e35740146105385780634c542f771461055857600080fd5b8063442559a214610496578063445738d8146104d957600080fd5b8063262907c5116102585780632ab2b52b1161023d5780632ab2b52b146103e65780632e06db96146104375780633c58e54d1461046757600080fd5b8063262907c51461038e57806329e0e160146103c657600080fd5b806303ec16d7146102f75780630d7daf3e1461031757806321506fff1461034e578063215619351461036e57600080fd5b366102f257336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102f0576040517faa39384e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561030357600080fd5b506102f06103123660046153db565b6109b0565b34801561032357600080fd5b50610337610332366004615412565b610b1d565b6040516103459291906154b2565b60405180910390f35b34801561035a57600080fd5b506102f06103693660046154e0565b610d48565b34801561037a57600080fd5b506102f0610389366004615412565b610faf565b34801561039a57600080fd5b506103ae6103a9366004615412565b6110f8565b6040516001600160a01b039091168152602001610345565b3480156103d257600080fd5b506102f06103e13660046154f9565b611184565b3480156103f257600080fd5b50610429610401366004615412565b6001600160a01b03909116600090815261177660209081526040808320938352929052205490565b604051908152602001610345565b34801561044357600080fd5b50610457610452366004615541565b6112dd565b6040519015158152602001610345565b34801561047357600080fd5b506104876104823660046154e0565b611348565b60405161034593929190615571565b3480156104a257600080fd5b506104296104b1366004615412565b6001600160a01b03909116600090815261119c60209081526040808320938352929052205490565b3480156104e557600080fd5b506104296104f436600461561f565b61143b565b34801561050557600080fd5b50610519610514366004615412565b6115fe565b604080516001600160a01b039093168352602083019190915201610345565b34801561054457600080fd5b506104296105533660046154e0565b611678565b34801561056457600080fd5b506103ae610573366004615412565b6116b0565b34801561058457600080fd5b506102f06105933660046156d6565b61173a565b3480156105a457600080fd5b506103ae6105b3366004615412565b611747565b3480156105c457600080fd5b506105fd6105d33660046154e0565b600090815261119a60205260409020546001600160a01b03811691600160a01b90910461ffff1690565b604080516001600160a01b03909316835261ffff909116602083015201610345565b61042961062d36600461570b565b611753565b34801561063e57600080fd5b506102f061064d366004615755565b611aff565b34801561065e57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103ae565b34801561069157600080fd5b506102f06106a03660046154e0565b611c08565b3480156106b157600080fd5b506102f06106c03660046156d6565b611c71565b3480156106d157600080fd5b506102f06106e03660046154e0565b611e91565b3480156106f157600080fd5b506102f0611f81565b34801561070657600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103ae565b6102f061073b3660046154e0565b612115565b34801561074c57600080fd5b506103ae61075b3660046154e0565b60009081526117776020526040908190206006810154600290910154600160a01b9182900467ffffffffffffffff1691900490911b73ffffffffffffffffffffffff0000000000000000161790565b3480156107b657600080fd5b506107ca6107c53660046154e0565b612121565b60405161034591906000610100820190506001600160a01b0380845116835260208401516020840152806040850151166040840152606084015160608401526080840151608084015260a084015160a08401528060c08501511660c08401525060e083015160e083015292915050565b6102f06108483660046156d6565b61221b565b34801561085957600080fd5b5061086d610868366004615412565b612228565b604080516001600160a01b039094168452602084019290925290820152606001610345565b34801561089e57600080fd5b506108b26108ad3660046156d6565b6122ad565b604051610345969594939291906157a1565b6102f06108d236600461570b565b612320565b6102f06108e53660046157f5565b612400565b3480156108f657600080fd5b5061042961090536600461582e565b6127a6565b34801561091657600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103ae565b34801561094957600080fd5b50610429610958366004615412565b6129a8565b34801561096957600080fd5b50610337610978366004615869565b612a0d565b34801561098957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006103ae565b80806000036109eb576040517f3a970fe600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815261177760205260409020610a02612fd5565b60028201546001600160a01b03908116911614610a615760028101546040517f9802550c0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024015b60405180910390fd5b600581015415610a9d576040517f5aea7c4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82816007015403610ada576040517f4b669ac700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007810183905560405183815284907f0c0f2662914f0cd1e952db2aa425901cb00e7c1f507687d22cb04e836d55d9c7906020015b60405180910390a250505050565b606080610b536001600160a01b0385167f2a55205a00000000000000000000000000000000000000000000000000000000612fe4565b15610c5d576040517f2a55205a0000000000000000000000000000000000000000000000000000000081526004810184905261271060248201526001600160a01b03851690632a55205a90619c409060440160408051808303818786fa93505050508015610bde575060408051601f3d908101601f19168201909252610bdb918101906158a0565b60015b15610c5d578015610c5a5760408051600180825281830190925290602080830190803683370190505093508184600081518110610c1d57610c1d6158fd565b6001600160a01b03929092166020928302919091018201526040805160018082528183019092529182810190803683370190505092505050610d41565b50505b610c906001600160a01b0385167fbb3bafd600000000000000000000000000000000000000000000000000000000612fe4565b15610d41576040517fbb3bafd6000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0385169063bb3bafd690619c40906024016000604051808303818786fa93505050508015610d1957506040513d6000823e601f3d908101601f19168201604052610d169190810190615a50565b60015b15610d4157815115801590610d2f575080518251145b15610d3e579092509050610d41565b50505b9250929050565b610d506130b3565b6000818152611777602090815260409182902082516101408101845281546001600160a01b039081168252600183015493820193909352600282015480841694820194909452600160a01b938490046bffffffffffffffffffffffff16606082015260038201546080820152600482015460a0820152600582015460c0820152600682015492831660e08201529290910467ffffffffffffffff1661010083015260070154610120820152610e03612fd5565b6001600160a01b031681604001516001600160a01b031614610e625760408082015190517f9802550c0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b60c081015115610e9e576040517f5aea7c4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516001600160a01b031660009081526117766020908152604080832082850180518552908352818420849055858452611777909252822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560018101839055600281018390556003810183905560048101839055600581018390556006810180547fffffffff000000000000000000000000000000000000000000000000000000001690556007019190915581519051610f5e9190613128565b610f758160000151826020015183604001516131ab565b60405182907f14b9c40404d5b41deb481f9a40b8aeb2bf4b47679b38cf757075a66ed510f7f190600090a250610fac6001610b8755565b50565b610fb76130b3565b6001600160a01b038083166000908152611f4e6020908152604080832085845290915281205490911690610fe9612fd5565b90506001600160a01b03821661102b576040517fc09f8e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b0316826001600160a01b031614611081576040517ff049b41a0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610a58565b6001600160a01b0384166000908152611f4e602090815260408083208684529091528120556110b18484836131ab565b60405183906001600160a01b038616907f70c7877531c04c7d9caa8a7eca127384f04e8a6ee58b63f778ce5401d8bcae4190600090a350506110f46001610b8755565b5050565b6001600160a01b0382166000908152612337602090815260408083208484529091528120805463ffffffff1642111561113557600091505061117e565b8054600182015470010000000000000000000000000000000090910460201b73ffffffffffffffffffffffffffffffff0000000016600160a01b90910463ffffffff16175b9150505b92915050565b61118c6130b3565b6001600160a01b0384166000908152612337602090815260408083208684529091529020805463ffffffff164211156111fc5780546040517f8c9e57cf00000000000000000000000000000000000000000000000000000000815263ffffffff9091166004820152602401610a58565b805464010000000090046bffffffffffffffffffffffff168211156112675780546040517f242373610000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff166004820152602401610a58565b60018101546001600160a01b038481169116146112c15760018101546040517fa7d95dc30000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b6112cb85856131b6565b506112d76001610b8755565b50505050565b600082815261119a60205260408120546001600160a01b0316801561134157600084815261119b602090815260408083206001600160a01b038716845290915290205460ff168061117a5750806001600160a01b0316836001600160a01b03161491505b5092915050565b600081815261119a6020908152604080832081516060808201845282546001600160a01b0381168352600160a01b900461ffff169482019490945260018201805494959485948594929084019161139e90615ab4565b80601f01602080910402602001604051908101604052809291908181526020018280546113ca90615ab4565b80156114175780601f106113ec57610100808354040283529160200191611417565b820191906000526020600020905b8154815290600101906020018083116113fa57829003601f168201915b50505091909252505050604081015181516020909201519097919650945092505050565b60008282808303611478576040517fe808160000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113888661ffff1611156114b8576040517f2b7b866100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611199805460010190819055925060006114d0612fd5565b90506040518060600160405280826001600160a01b031681526020018861ffff1681526020018a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525093909452505086815261119a6020908152604091829020845181549286015161ffff16600160a01b027fffffffffffffffffffff000000000000000000000000000000000000000000009093166001600160a01b039091161791909117815590830151909150600182019061159d9082615b4d565b50905050806001600160a01b0316847f9eee3ce0e6f7eeabd69ecf363898e9f490dbfda9ad953e1019a2c6aeceb4a7ef8b8b8b6040516115df93929190615c0d565b60405180910390a36115f28487876134b1565b50505095945050505050565b6001600160a01b038083166000908152611f4e6020908152604080832085845290915281205490911690816116365750600019610d41565b506001600160a01b03929092166000908152611f4e6020908152604080832093835292905220549091600160a01b9091046bffffffffffffffffffffffff1690565b6000818152611777602052604081206005810154820361169c576007015492915050565b6116a9816007015461362d565b9392505050565b6040517f40c1a064000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b038416906340c1a06490619c40906024016020604051808303818786fa158015611715573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116a99190615c4a565b6112d783838360006127a6565b60006116a9838361364f565b600061176085858561365b565b1561176d57506000611af7565b61177785856136ce565b156117ae576040517f83a483f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0385166000908152612337602090815260408083208784529091528120906117db612fd5565b825490915063ffffffff164211156118a2576040517f4ec58ed70000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152602482018790527f00000000000000000000000000000000000000000000000000000000000000001690634ec58ed790349060440160206040518083038185885af1158015611876573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061189b9190615c67565b92506119f0565b81546000906118c69064010000000090046bffffffffffffffffffffffff1661362d565b905080861015611905576040517fe40a30e600000000000000000000000000000000000000000000000000000000815260048101829052602401610a58565b600183015483546040517f5fdec5610000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015263ffffffff821660248201526401000000009091046bffffffffffffffffffffffff1660448201528382166064820152608481018890527f000000000000000000000000000000000000000000000000000000000000000090911690635fdec56190349060a40160206040518083038185885af11580156119c7573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906119ec9190615c67565b9350505b600182018054835463ffffffff8681167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909216919091176401000000006bffffffffffffffffffffffff8a1602176fffffffffffffffffffffffffffffffff908116602089811c909216700100000000000000000000000000000000021786556001600160a01b038581167fffffffffffffffff0000000000000000000000000000000000000000000000009094168417600160a01b938a1693909302929092179093556040805189815293840187905291928992918b16917ece0a712e4e277ac7b34942865f0de7a5629dffe0539b70423ad5ff1ed6ab42910160405180910390a450505b949350505050565b600083815261119a602052604090205483906001600160a01b0316611b22612fd5565b6001600160a01b0316816001600160a01b031614611bb7576001600160a01b038116611b7a576040517f0c77a95c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fb39cb29b0000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610a58565b83836000819003611bf4576040517fe808160000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bff8787876134b1565b50505050505050565b611c106130b3565b600081815261177760205260408120600501549003611c5b576040517f4b6ad8fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c6681600061371a565b610fac6001610b8755565b611c796130b3565b611c848383836139bb565b611e81576bffffffffffffffffffffffff811115611cce576040517f35ec82cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038381166000908152611f4e60209081526040808320868452909152902080549091811690600160a01b90046bffffffffffffffffffffffff1683148015611d2557506001600160a01b03811615155b15611d5c576040517fb6950f3600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546001600160a01b0316600160a01b6bffffffffffffffffffffffff8516021782556000611d89612fd5565b90506001600160a01b038216611dd957611da38686613a23565b82547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038216178355611e2f565b806001600160a01b0316826001600160a01b031614611e2f576040517f697d918e0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610a58565b806001600160a01b031685876001600160a01b03167ffcc77ea8bdcce862f43b7fb00fe6b0eb90d6aeead27d3800d9257cf7a05f9d9687604051611e7591815260200190565b60405180910390a45050505b611e8c6001610b8755565b505050565b600081815261119a602052604090205481906001600160a01b0316611eb4612fd5565b6001600160a01b0316816001600160a01b031614611f0c576001600160a01b038116611b7a576040517f0c77a95c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815261119a6020526040812080547fffffffffffffffffffff0000000000000000000000000000000000000000000016815590611f4f600183018261538d565b505060405183907f2a9aeaf340ca0da469c1f7e3d513c0e6c9cd287016f29d257a4ef70e13dc441c90600090a2505050565b600054610100900460ff1615808015611fa15750600054600160ff909116105b80611fbb5750303b158015611fbb575060005460ff166001145b612047576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a58565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156120a557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6120b0600161138d55565b8015610fac57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b610fac81346000612400565b61218460405180610100016040528060006001600160a01b031681526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600081525090565b506000908152611777602090815260409182902082516101008101845281546001600160a01b03908116825260018301549382019390935260028201548316938101939093527f000000000000000000000000000000000000000000000000000000000000000060608401526103846080840152600581015460a0840152600681015490911660c08301526007015460e082015290565b611e8c8383836000612320565b6001600160a01b038216600090815261233760209081526040808320848452909152812080548291829163ffffffff16421115612270576000806000935093509350506122a6565b600181015490546001600160a01b03909116935063ffffffff8116925064010000000090046bffffffffffffffffffffffff1690505b9250925092565b6000806060806000806122c08989613a2d565b90506122d18989838a600080613acb565b5093995091965094509250600090505b8351811015612313578381815181106122fc576122fc6158fd565b6020026020010151860195508060010190506122e1565b5093975093979195509350565b6001600160a01b0384166000908152611f4e6020908152604080832086845290915290208054600160a01b90046bffffffffffffffffffffffff168310156123ad5780546040517f16b5016f000000000000000000000000000000000000000000000000000000008152600160a01b9091046bffffffffffffffffffffffff166004820152602401610a58565b80546001600160a01b03166123ee576040517fda48e18400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123f9858584614132565b5050505050565b6124086130b3565b6000838152611777602052604081206007810154909103612455576040517f125197d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3483101561248f576040517fe2bbc1e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005810154600061249e612fd5565b90506001600160a01b0384161515806124b657508115155b15612529576002830180546001600160a01b0316604086901c6bffffffffffffffffffffffff16600160a01b908102919091179091556006840180547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1667ffffffffffffffff87169092029190911790555b816000036125fe5782600701548510156125775782600701546040517f31e6f71c000000000000000000000000000000000000000000000000000000008152600401610a5891815260200190565b82546001840154612591916001600160a01b031690614271565b600783018590556006830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790557f00000000000000000000000000000000000000000000000000000000000000004201600584018190559150612747565b61260782421190565b15612641576040517f3feeb88d00000000000000000000000000000000000000000000000000000000815260048101839052602401610a58565b60068301546001600160a01b0380831691160361268a576040517fe140576800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612699846007015461362d565b9050808610156126d8576040517fcd698a1900000000000000000000000000000000000000000000000000000000815260048101829052602401610a58565b50600783018054600685018054928890556001600160a01b038481167fffffffffffffffffffffffff000000000000000000000000000000000000000085161790915590911642610384018085101561273657600586018190559350835b506127448183614e2061427b565b50505b6127528560006143b7565b60408051868152602081018490526001600160a01b0383169188917f26ea3ebbda62eb1baef13e1c237dddd956c87f80b2801f2616d806d52557b121910160405180910390a3505050611e8c6001610b8755565b60006127b06130b3565b82806000036127eb576040517f3a970fe600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61138d80546001810190915591506128038686613a23565b6001600160a01b03861660009081526117766020908152604080832088845290915290205415612889576001600160a01b038616600090815261177660209081526040808320888452909152908190205490517f7618a0030000000000000000000000000000000000000000000000000000000081526004810191909152602401610a58565b6000612893612fd5565b6001600160a01b038881166000818152611776602090815260408083208c84528252808320899055888352611777909152902080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081169092178155600181018a90556002810180549092169284169290921790556007810187905590915061291f8888876144a2565b604080517f0000000000000000000000000000000000000000000000000000000000000000815261038460208201529081018790526060810185905287906001600160a01b03808b1691908516907f1062dd3b35f12b4064331244d00f40c1d4831965e4285654157a2409c6217cff9060800160405180910390a4505050611af76001610b8755565b6001600160a01b0382166000908152612337602090815260408083208484529091528120805463ffffffff164211612a035780546129fb9064010000000090046bffffffffffffffffffffffff1661362d565b91505061117e565b5060019392505050565b6040517fde5488af0000000000000000000000000000000000000000000000000000000081526001600160a01b03848116600483015260609182917f0000000000000000000000000000000000000000000000000000000000000000169063de5488af90619c40906024016020604051808303818786fa93505050508015612ab2575060408051601f3d908101601f19168201909252612aaf91810190615c4a565b60015b15612d0357856001600160a01b0316816001600160a01b031614612d0157945084612b066001600160a01b0382167f2a55205a00000000000000000000000000000000000000000000000000000000612fe4565b15612c11576040517f2a55205a0000000000000000000000000000000000000000000000000000000081526004810186905261271060248201526001600160a01b03871690632a55205a90619c409060440160408051808303818786fa93505050508015612b91575060408051601f3d908101601f19168201909252612b8e918101906158a0565b60015b15612c11578015612c0e5760408051600180825281830190925290602080830190803683370190505094508185600081518110612bd057612bd06158fd565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337019050509350505050612fcd565b50505b8251158015612c4e5750612c4e6001600160a01b0387167fbb3bafd600000000000000000000000000000000000000000000000000000000612fe4565b15612d01576040517fbb3bafd6000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b0387169063bb3bafd690619c40906024016000604051808303818786fa93505050508015612cd757506040513d6000823e601f3d908101601f19168201604052612cd49190810190615a50565b60015b15612d0157815115801590612ced575080518251145b15612cfe579093509150612fcd9050565b50505b505b612d366001600160a01b0386167fb779958400000000000000000000000000000000000000000000000000000000612fe4565b15612e6b576040517fb9c4d9fb000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b0386169063b9c4d9fb90619c40906024016000604051808303818786fa93505050508015612dbf57506040513d6000823e601f3d908101601f19168201604052612dbc9190810190615c80565b60015b15612e6b57805115612e69576040517f0ebd4c7f000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03871690630ebd4c7f90619c40906024016000604051808303818786fa93505050508015612e4f57506040513d6000823e601f3d908101601f19168201604052612e4c9190810190615cb5565b60015b15612e69578051825103612e67579092509050612fcd565b505b505b6001600160a01b03831615612ee95760408051600180825281830190925290602080830190803683370190505091508282600081518110612eae57612eae6158fd565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337019050509050612fcd565b846001600160a01b0316638da5cb5b619c406040518263ffffffff1660e01b81526004016020604051808303818786fa93505050508015612f47575060408051601f3d908101601f19168201909252612f4491810190615c4a565b60015b15612fcd576001600160a01b03811615612fcb5760408051600180825281830190925290602080830190803683370190505092508083600081518110612f8f57612f8f6158fd565b6001600160a01b039290921660209283029190910182015260408051600180825281830190925291828101908036833701905050915050612fcd565b505b935093915050565b6000612fdf6145d7565b905090565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d9150600051905082801561309c575060208210155b80156130a85750600081115b979650505050505050565b6002610b875403613120576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a58565b6002610b8755565b6001600160a01b038216600090815261119c602090815260408083208484529091529020548015611e8c576001600160a01b038316600081815261119c60209081526040808320868452909152808220829055518392859290917f2ea2946ee16c4a1d0ec58464194022e54432a6d7db359835ddf283555f2c8eee9190a4505050565b611e8c838383614634565b6001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046bffffffffffffffffffffffff9081168489019081527001000000000000000000000000000000009093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c5297909952989094557fffffffffffffffff00000000000000000000000000000000000000000000000090961690965591518251955193517f4dc8fb3c000000000000000000000000000000000000000000000000000000008152908716600482015294909316602485015291166044830152917f00000000000000000000000000000000000000000000000000000000000000001690634dc8fb3c90606401600060405180830381600087803b15801561331357600080fd5b505af1158015613327573d6000803e3d6000fd5b505050506000613335612fd5565b60608301516040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b0380841660048301529182166024820152604481018690529192508516906323b872dd90606401600060405180830381600087803b1580156133a857600080fd5b505af19250505080156133b9575060015b6133cd576133cd848484606001518461466b565b600080600061342b87878688602001516bffffffffffffffffffffffff166134238a604001518b6080015173ffffffffffffffffffffffffffffffff0000000060209290921b9190911663ffffffff9091161790565b600080614677565b92509250925084606001516001600160a01b031686886001600160a01b03167f1cb8adb37d6d35e94cd0695ca39895b84371864713f5ca7eada52af9ff23744b878787876040516134a094939291906001600160a01b0394909416845260208401929092526040830152606082015260800190565b60405180910390a450505050505050565b60005b818110156135ed5760008383838181106134d0576134d06158fd565b90506020020160208101906134e59190615cea565b600086815261119b602090815260408083206001600160a01b038516845290915290205490915060ff1615613546576040517f667888ec00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61354e612fd5565b6001600160a01b0316816001600160a01b031603613598576040517f43e2197f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815261119b602090815260408083206001600160a01b0390941683529290522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155016134b4565b50827fd5a31bd2d34d303249ac7f54bfc7578390f90f5d39cb39813f67190fa36f5c178383604051613620929190615d07565b60405180910390a2505050565b6000600a8204808203613645576116a9836001615d79565b6116a98382615d79565b60006116a9838361489e565b6001600160a01b038084166000908152611f4e6020908152604080832086845290915281208054919290911615806136a857508054600160a01b90046bffffffffffffffffffffffff1683105b156136b75760009150506116a9565b6136c385856000614132565b506001949350505050565b6001600160a01b038216600090815261177660209081526040808320848452909152812054801580159061117a5750600090815261177760205260409020600501544211159392505050565b6000828152611777602090815260409182902082516101408101845281546001600160a01b039081168252600183015493820193909352600282015480841694820194909452600160a01b938490046bffffffffffffffffffffffff16606082015260038201546080820152600482015460a0820152600582015460c08201819052600683015493841660e08301529390920467ffffffffffffffff1661010083015260070154610120820152904211613808578060c001516040517f3a017f60000000000000000000000000000000000000000000000000000000008152600401610a5891815260200190565b60008061381d836000015184602001516148d4565b84516001600160a01b03166000908152611776602090815260408083208289015184528252808320839055898352611777909152812080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560018101829055600281018290556003810182905560048101829055600581018290556006810180547fffffffff00000000000000000000000000000000000000000000000000000000169055600701559092509050836138ed576138ed836000015184602001518560e001516000614950565b60008060006139428660000151876020015188604001518961012001518a610100015167ffffffffffffffff1660408c606001516bffffffffffffffffffffffff166001600160a01b0316901b178a8a614677565b9250925092508560e001516001600160a01b031686604001516001600160a01b0316897f2edb0e99c6ac35be6731dab554c1d1fa1b7beb675090dbb09fb14e615aca1c4a8686866040516139a9939291909283526020830191909152604082015260600190565b60405180910390a45050505050505050565b6001600160a01b0383166000908152612337602090815260408083208584529091528120805463ffffffff16421180613a0a5750805464010000000090046bffffffffffffffffffffffff1683115b15613a195760009150506116a9565b6136c385856131b6565b6110f48282614a18565b6000613a39838361364f565b90506001600160a01b03811661117e576040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015613aa7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a99190615c4a565b6000606080828080612710613b007f00000000000000000000000000000000000000000000000000000000000000008b615d8c565b613b0a9190615da3565b6040517f4c542f770000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e90529197506000917f00000000000000000000000000000000000000000000000000000000000000001690634c542f7790604401602060405180830381865afa925050508015613bb2575060408051601f3d908101601f19168201909252613baf91810190615c4a565b60015b15613bba5790505b6040517f0d7daf3e0000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e90527f00000000000000000000000000000000000000000000000000000000000000001690630d7daf3e90604401600060405180830381865afa925050508015613c6157506040513d6000823e601f3d908101601f19168201604052613c5e9190810190615a50565b60015b15613c6c5790965094505b8551600003613d2f576040517fefef76f80000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e905282811660448301527f0000000000000000000000000000000000000000000000000000000000000000169063efef76f890606401600060405180830381865afa925050508015613d2457506040513d6000823e601f3d908101601f19168201604052613d219190810190615a50565b60015b15613d2f5790965094505b8551151580613d5b57507f00000000000000000000000000000000000000000000000000000000000000005b1561406b5760007f000000000000000000000000000000000000000000000000000000000000000015613e0657878b0390508651600003613e015760408051600180825281830190925290602080830190803683370190505096508b87600081518110613dca57613dca6158fd565b6001600160a01b03929092166020928302919091018201526040805160018082528183019092529182810190803683370190505095505b613e87565b816001600160a01b03168c6001600160a01b03161480613e5c5750865115801590613e5c575086600081518110613e3f57613e3f6158fd565b60200260200101516001600160a01b03168c6001600160a01b0316145b15613e6a5750868a03613e87565b50600a8a0480613e7a898d615dde565b613e849190615dde565b94505b613e92876005614aab565b613e9d866005614aab565b61ffff891615613eea57612710613eb861ffff8b168d615d8c565b613ec29190615da3565b925084600003613edd57613ed68382615dde565b9050613eea565b613ee78386615dde565b94505b6000600188511115613fc45760005b8851811015613f9a578d6001600160a01b0316898281518110613f1e57613f1e6158fd565b60200260200101516001600160a01b031603613f3d5760009692909201915b6000198214613f9257612710888281518110613f5b57613f5b6158fd565b60200260200101511115613f73576000199150613f92565b878181518110613f8557613f856158fd565b6020026020010151820191505b600101613ef9565b50801580613fa9575060001981145b15613fc457613fb9886001614aab565b613fc4876001614aab565b600060015b8951811015614039576000838a8381518110613fe757613fe76158fd565b602002602001015186613ffa9190615d8c565b6140049190615da3565b90506140108184615d79565b9250808a8381518110614025576140256158fd565b602090810291909101015250600101613fc9565b506140448184615dde565b88600081518110614057576140576158fd565b6020026020010181815250505050506140a4565b868a03935061ffff8816156140a45761271061408b61ffff8a168c615d8c565b6140959190615da3565b91506140a18285615dde565b93505b6001600160a01b038916158015906140d557506140bf612fd5565b6001600160a01b0316896001600160a01b031614155b80156140f357508a6001600160a01b0316896001600160a01b031614155b80156141115750806001600160a01b0316896001600160a01b031614155b156141225760648a04925082870396505b5096509650965096509650969050565b61413a6130b3565b6001600160a01b038381166000908152611f4e60209081526040808320868452808352818420825180840190935280549586168352600160a01b9095046bffffffffffffffffffffffff1682840152868452909152915561419b8484614ab8565b6141b881602001516bffffffffffffffffffffffff1660016143b7565b60006141c2612fd5565b90506141d1858583600061466b565b60008060006141fc8888876000015188602001516bffffffffffffffffffffffff168a600080614677565b8751604080516001600160a01b038a81168252602082018790529181018590526060810184905294975092955090935081169189918b16907fd28c0a7dd63bc853a4e36306655da9f8c0b29ff9d0605bb976ae420e46a999309060800160405180910390a45050505050611e8c6001610b8755565b6110f48282614b05565b8160000361428857505050565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d80600081146142d8576040519150601f19603f3d011682016040523d82523d6000602084013e6142dd565b606091505b50509050806112d7576040517faa67c9190000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063aa67c9199085906024016000604051808303818588803b15801561436357600080fd5b505af1158015614377573d6000803e3d6000fd5b5050505050836001600160a01b03167fa2201512569adb2d513531dfd69b66df50bd5cffb8c1bbe65a4611f9e1eadbd184604051610b0f91815260200190565b34821115614474577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663452f2b8f6143f6612fd5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b03909116600482015234850360248201526044015b600060405180830381600087803b15801561445857600080fd5b505af115801561446c573d6000803e3d6000fd5b505050505050565b80801561448057503482105b156110f4576110f4823403614493612fd5565b6001600160a01b031690614b19565b8015611e8c57600081815261119a6020526040902080546001600160a01b03166144f8576040517f0c77a95c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000614502612fd5565b600084815261119b602090815260408083206001600160a01b038516845290915290205490915060ff16158015614546575081546001600160a01b03828116911614155b1561457d576040517f6e93a35400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516600081815261119c60209081526040808320888452909152808220869055518592879290917fb17e0c916df75a12480835f00b3927cb871bbe00bacf819f81a1d92f9ff7f38d9190a45050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016810361463157507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec36013560601c5b90565b6001600160a01b038084166000908152611f4e6020908152604080832086845290915290205416806112d7576112d7848484614c66565b6112d784848484614c9c565b60008060008660000361469257506000915081905080614891565b6060806000806146a68e8e8e8e8e8d613acb565b8451959c5091995092975090955090935091506000906001146146cb57614e206146d0565b620334505b905060005b85518110156147445761471b8682815181106146f3576146f36158fd565b602002602001015186838151811061470d5761470d6158fd565b60200260200101518461427b565b84818151811061472d5761472d6158fd565b6020026020010151880197508060010190506146d5565b506147528d87614e2061427b565b61477f7f000000000000000000000000000000000000000000000000000000000000000089614e2061427b565b82156147fa576147928b84614e2061427b565b8d8f6001600160a01b03167f141b92fd9766c80ab120598ea2f6be9802470ec59b5446dd9bf46214ead8d08e8d8660006040516147ed939291906001600160a01b039390931683526020830191909152604082015260600190565b60405180910390a3968201965b6001600160a01b038a161561488b57811561483257856000036148205795810195614825565b948101945b6148328a83614e2061427b565b8d8f6001600160a01b03167f27a4dd4ff659a9e6354fb079b2208365e5b83f55c22a4150eee2bca89501cb988c856040516148829291906001600160a01b03929092168252602082015260400190565b60405180910390a35b50505050505b9750975097945050505050565b6001600160a01b038083166000908152611f4e60209081526040808320858452909152902054168061117e576116a98383614d39565b6001600160a01b038216600090815261119c602090815260408083208484529091528120548190801561494857600081815261119a60209081526040808320546001600160a01b03898116855261119c84528285208986529093529083209290925581169350600160a01b900461ffff1691505b509250929050565b6001600160a01b03811615614991576040517f57a016b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152604482018590528516906323b872dd90606401600060405180830381600087803b1580156149fa57600080fd5b505af1158015614a0e573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680614a4e57611e8c8383614d7a565b614a56612fd5565b6001600160a01b0316816001600160a01b031614611e8c576040517f32f3b0330000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610a58565b80825111156110f4579052565b6001600160a01b0382166000908152612337602090815260408083208484529091529020614ae4612fd5565b60018201546001600160a01b03918216911603611e8c57611e8c8383614e9b565b614b0f8282614e9b565b6110f4828261507c565b80471015614b83576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610a58565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614bd0576040519150601f19603f3d011682016040523d82523d6000602084013e614bd5565b606091505b5050905080611e8c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610a58565b6001600160a01b0383166000908152611776602090815260408083208584529091528120549003611e8c57611e8c8383836150b5565b6001600160a01b038085166000908152611f4e60209081526040808320878452909152902054168015614d2d57816001600160a01b0316816001600160a01b031614614d1f576040517f32f3b0330000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610a58565b60009150614d2d85856150c2565b6123f985858585615115565b6001600160a01b038083166000908152611776602090815260408083208584528252808320548352611777909152902060020154168061117e5760006116a9565b6001600160a01b03821660009081526117766020908152604080832084845290915281205490819003614db157611e8c8383615325565b60008181526117776020526040812090614dc9612fd5565b90508160050154600003614e365760028201546001600160a01b03828116911614614e315760028201546040517fe64526ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b6123f9565b60068201546001600160a01b03828116911614614e905760068201546040517fe64526ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b6123f983600161371a565b6001600160a01b03821660009081526123376020908152604080832084845290915290205463ffffffff1642116110f4576001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046bffffffffffffffffffffffff9081168489019081527001000000000000000000000000000000009093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c5297909952989094557fffffffffffffffff00000000000000000000000000000000000000000000000090961690965591518251955193517f345db493000000000000000000000000000000000000000000000000000000008152908716600482015294909316602485015291166044830152917f0000000000000000000000000000000000000000000000000000000000000000169063345db49390606401600060405180830381600087803b15801561502957600080fd5b505af115801561503d573d6000803e3d6000fd5b50506040518492506001600160a01b03861691507f30c264456cbd17f5f67d7534654161414f34c0e6cc1b7500e169b7a7aea4afc090600090a3505050565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020805490911615611e8c57611e8c83836150c2565b611e8c838383600061466b565b6001600160a01b0382166000818152611f4e60209081526040808320858452909152808220829055518392917faa6271d89a385571e237d3e7254ccc7c09f68055e6e9b410ed08233a8b9a05cf91a35050565b6001600160a01b03841660009081526117766020908152604080832086845290915290205480156153195760008181526117776020526040812060058101549091036152ae576001600160a01b03831615801590615183575060028101546001600160a01b03848116911614155b156151cb5760028101546040517fe64526ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b6001600160a01b0386166000908152611776602090815260408083208884528252808320839055848352611777909152812080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560018101829055600281018290556003810182905560048101829055600581018290556006810180547fffffffff000000000000000000000000000000000000000000000000000000001690556007015561527e8686613128565b60405182907f5603897cc9b1e866f3f7395ffc6638776041f21c094d0b4e748ff44c407fa36290600090a2615313565b60068101546001600160a01b038481169116146153085760068101546040517fe64526ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b61531382600161371a565b60009250505b6123f985858585614950565b816001600160a01b03166323b872dd61533c612fd5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b0390911660048201523060248201526044810184905260640161443e565b50805461539990615ab4565b6000825580601f106153a9575050565b601f016020900490600052602060002090810190610fac91905b808211156153d757600081556001016153c3565b5090565b600080604083850312156153ee57600080fd5b50508035926020909101359150565b6001600160a01b0381168114610fac57600080fd5b6000806040838503121561542557600080fd5b8235615430816153fd565b946020939093013593505050565b600081518084526020808501945080840160005b838110156154775781516001600160a01b031687529582019590820190600101615452565b509495945050505050565b600081518084526020808501945080840160005b8381101561547757815187529582019590820190600101615496565b6040815260006154c5604083018561543e565b82810360208401526154d78185615482565b95945050505050565b6000602082840312156154f257600080fd5b5035919050565b6000806000806080858703121561550f57600080fd5b843561551a816153fd565b9350602085013592506040850135615531816153fd565b9396929550929360600135925050565b6000806040838503121561555457600080fd5b823591506020830135615566816153fd565b809150509250929050565b606081526000845180606084015260005b8181101561559f5760208188018101516080868401015201615582565b506000608082850101526080601f19601f8301168401019150506001600160a01b038416602083015261ffff83166040830152949350505050565b60008083601f8401126155ec57600080fd5b50813567ffffffffffffffff81111561560457600080fd5b6020830191508360208260051b8501011115610d4157600080fd5b60008060008060006060868803121561563757600080fd5b853567ffffffffffffffff8082111561564f57600080fd5b818801915088601f83011261566357600080fd5b81358181111561567257600080fd5b89602082850101111561568457600080fd5b602092830197509550908701359061ffff821682146156a257600080fd5b909350604087013590808211156156b857600080fd5b506156c5888289016155da565b969995985093965092949392505050565b6000806000606084860312156156eb57600080fd5b83356156f6816153fd565b95602085013595506040909401359392505050565b6000806000806080858703121561572157600080fd5b843561572c816153fd565b93506020850135925060408501359150606085013561574a816153fd565b939692955090935050565b60008060006040848603121561576a57600080fd5b83359250602084013567ffffffffffffffff81111561578857600080fd5b615794868287016155da565b9497909650939450505050565b86815285602082015260c0604082015260006157c060c083018761543e565b82810360608401526157d28187615482565b9150508360808301526001600160a01b03831660a0830152979650505050505050565b60008060006060848603121561580a57600080fd5b83359250602084013591506040840135615823816153fd565b809150509250925092565b6000806000806080858703121561584457600080fd5b843561584f816153fd565b966020860135965060408601359560600135945092505050565b60008060006060848603121561587e57600080fd5b8335615889816153fd565b9250602084013591506040840135615823816153fd565b600080604083850312156158b357600080fd5b82516158be816153fd565b6020939093015192949293505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715615955576159556158ce565b604052919050565b600067ffffffffffffffff821115615977576159776158ce565b5060051b60200190565b600082601f83011261599257600080fd5b815160206159a76159a28361595d565b61592c565b82815260059290921b840181019181810190868411156159c657600080fd5b8286015b848110156159ea5780516159dd816153fd565b83529183019183016159ca565b509695505050505050565b600082601f830112615a0657600080fd5b81516020615a166159a28361595d565b82815260059290921b84018101918181019086841115615a3557600080fd5b8286015b848110156159ea5780518352918301918301615a39565b60008060408385031215615a6357600080fd5b825167ffffffffffffffff80821115615a7b57600080fd5b615a8786838701615981565b93506020850151915080821115615a9d57600080fd5b50615aaa858286016159f5565b9150509250929050565b600181811c90821680615ac857607f821691505b602082108103615b01577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f821115611e8c57600081815260208120601f850160051c81016020861015615b2e5750805b601f850160051c820191505b8181101561446c57828155600101615b3a565b815167ffffffffffffffff811115615b6757615b676158ce565b615b7b81615b758454615ab4565b84615b07565b602080601f831160018114615bb05760008415615b985750858301515b600019600386901b1c1916600185901b17855561446c565b600085815260208120601f198616915b82811015615bdf57888601518255948401946001909101908401615bc0565b5085821015615bfd5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081528260408201528284606083013760006060848301015260006060601f19601f860116830101905061ffff83166020830152949350505050565b600060208284031215615c5c57600080fd5b81516116a9816153fd565b600060208284031215615c7957600080fd5b5051919050565b600060208284031215615c9257600080fd5b815167ffffffffffffffff811115615ca957600080fd5b61117a84828501615981565b600060208284031215615cc757600080fd5b815167ffffffffffffffff811115615cde57600080fd5b61117a848285016159f5565b600060208284031215615cfc57600080fd5b81356116a9816153fd565b60208082528181018390526000908460408401835b868110156159ea578235615d2f816153fd565b6001600160a01b031682529183019190830190600101615d1c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561117e5761117e615d4a565b808202811582820484141761117e5761117e615d4a565b600082615dd9577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b8181038181111561117e5761117e615d4a56fea264697066735822122077d50ffac51a93cc0246c53b240f3653f9282905120beac5a5e9e9e2ca93892964736f6c6343000813003300000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb600000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d0000000000000000000000000000000000000000000000000000000000015180000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e3
Deployed Bytecode
0x6080604052600436106102895760003560e01c80636a90a82711610153578063a59ac6dd116100cb578063beb5127c1161007f578063e5d1e72311610064578063e5d1e7231461093d578063efef76f81461095d578063f7a2da231461097d57600080fd5b8063beb5127c146108ea578063daa351d41461090a57600080fd5b8063af1e1de3116100b0578063af1e1de314610892578063b01ef608146108c4578063b6aff8c1146108d757600080fd5b8063a59ac6dd1461083a578063ac71045e1461084d57600080fd5b80638129fc1c116101225780639979ef45116101075780639979ef451461072d5780639e64ba6c146107405780639e79b41f146107aa57600080fd5b80638129fc1c146106e5578063895633ba146106fa57600080fd5b80636a90a827146106525780637430e0c614610685578063798bac8d146106a55780637b3a5884146106c557600080fd5b8063442559a2116102015780634ce6931a116101b557806355daed3e1161019a57806355daed3e146105b8578063614b151c1461061f5780636512ed2d1461063257600080fd5b80634ce6931a146105785780634fca06c61461059857600080fd5b80634635256e116101e65780634635256e146104f957806347e35740146105385780634c542f771461055857600080fd5b8063442559a214610496578063445738d8146104d957600080fd5b8063262907c5116102585780632ab2b52b1161023d5780632ab2b52b146103e65780632e06db96146104375780633c58e54d1461046757600080fd5b8063262907c51461038e57806329e0e160146103c657600080fd5b806303ec16d7146102f75780630d7daf3e1461031757806321506fff1461034e578063215619351461036e57600080fd5b366102f257336001600160a01b037f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d5044316146102f0576040517faa39384e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561030357600080fd5b506102f06103123660046153db565b6109b0565b34801561032357600080fd5b50610337610332366004615412565b610b1d565b6040516103459291906154b2565b60405180910390f35b34801561035a57600080fd5b506102f06103693660046154e0565b610d48565b34801561037a57600080fd5b506102f0610389366004615412565b610faf565b34801561039a57600080fd5b506103ae6103a9366004615412565b6110f8565b6040516001600160a01b039091168152602001610345565b3480156103d257600080fd5b506102f06103e13660046154f9565b611184565b3480156103f257600080fd5b50610429610401366004615412565b6001600160a01b03909116600090815261177660209081526040808320938352929052205490565b604051908152602001610345565b34801561044357600080fd5b50610457610452366004615541565b6112dd565b6040519015158152602001610345565b34801561047357600080fd5b506104876104823660046154e0565b611348565b60405161034593929190615571565b3480156104a257600080fd5b506104296104b1366004615412565b6001600160a01b03909116600090815261119c60209081526040808320938352929052205490565b3480156104e557600080fd5b506104296104f436600461561f565b61143b565b34801561050557600080fd5b50610519610514366004615412565b6115fe565b604080516001600160a01b039093168352602083019190915201610345565b34801561054457600080fd5b506104296105533660046154e0565b611678565b34801561056457600080fd5b506103ae610573366004615412565b6116b0565b34801561058457600080fd5b506102f06105933660046156d6565b61173a565b3480156105a457600080fd5b506103ae6105b3366004615412565b611747565b3480156105c457600080fd5b506105fd6105d33660046154e0565b600090815261119a60205260409020546001600160a01b03811691600160a01b90910461ffff1690565b604080516001600160a01b03909316835261ffff909116602083015201610345565b61042961062d36600461570b565b611753565b34801561063e57600080fd5b506102f061064d366004615755565b611aff565b34801561065e57600080fd5b507f000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e36103ae565b34801561069157600080fd5b506102f06106a03660046154e0565b611c08565b3480156106b157600080fd5b506102f06106c03660046156d6565b611c71565b3480156106d157600080fd5b506102f06106e03660046154e0565b611e91565b3480156106f157600080fd5b506102f0611f81565b34801561070657600080fd5b507f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436103ae565b6102f061073b3660046154e0565b612115565b34801561074c57600080fd5b506103ae61075b3660046154e0565b60009081526117776020526040908190206006810154600290910154600160a01b9182900467ffffffffffffffff1691900490911b73ffffffffffffffffffffffff0000000000000000161790565b3480156107b657600080fd5b506107ca6107c53660046154e0565b612121565b60405161034591906000610100820190506001600160a01b0380845116835260208401516020840152806040850151166040840152606084015160608401526080840151608084015260a084015160a08401528060c08501511660c08401525060e083015160e083015292915050565b6102f06108483660046156d6565b61221b565b34801561085957600080fd5b5061086d610868366004615412565b612228565b604080516001600160a01b039094168452602084019290925290820152606001610345565b34801561089e57600080fd5b506108b26108ad3660046156d6565b6122ad565b604051610345969594939291906157a1565b6102f06108d236600461570b565b612320565b6102f06108e53660046157f5565b612400565b3480156108f657600080fd5b5061042961090536600461582e565b6127a6565b34801561091657600080fd5b507f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d6103ae565b34801561094957600080fd5b50610429610958366004615412565b6129a8565b34801561096957600080fd5b50610337610978366004615869565b612a0d565b34801561098957600080fd5b507f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb66103ae565b80806000036109eb576040517f3a970fe600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815261177760205260409020610a02612fd5565b60028201546001600160a01b03908116911614610a615760028101546040517f9802550c0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526024015b60405180910390fd5b600581015415610a9d576040517f5aea7c4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82816007015403610ada576040517f4b669ac700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007810183905560405183815284907f0c0f2662914f0cd1e952db2aa425901cb00e7c1f507687d22cb04e836d55d9c7906020015b60405180910390a250505050565b606080610b536001600160a01b0385167f2a55205a00000000000000000000000000000000000000000000000000000000612fe4565b15610c5d576040517f2a55205a0000000000000000000000000000000000000000000000000000000081526004810184905261271060248201526001600160a01b03851690632a55205a90619c409060440160408051808303818786fa93505050508015610bde575060408051601f3d908101601f19168201909252610bdb918101906158a0565b60015b15610c5d578015610c5a5760408051600180825281830190925290602080830190803683370190505093508184600081518110610c1d57610c1d6158fd565b6001600160a01b03929092166020928302919091018201526040805160018082528183019092529182810190803683370190505092505050610d41565b50505b610c906001600160a01b0385167fbb3bafd600000000000000000000000000000000000000000000000000000000612fe4565b15610d41576040517fbb3bafd6000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0385169063bb3bafd690619c40906024016000604051808303818786fa93505050508015610d1957506040513d6000823e601f3d908101601f19168201604052610d169190810190615a50565b60015b15610d4157815115801590610d2f575080518251145b15610d3e579092509050610d41565b50505b9250929050565b610d506130b3565b6000818152611777602090815260409182902082516101408101845281546001600160a01b039081168252600183015493820193909352600282015480841694820194909452600160a01b938490046bffffffffffffffffffffffff16606082015260038201546080820152600482015460a0820152600582015460c0820152600682015492831660e08201529290910467ffffffffffffffff1661010083015260070154610120820152610e03612fd5565b6001600160a01b031681604001516001600160a01b031614610e625760408082015190517f9802550c0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b60c081015115610e9e576040517f5aea7c4700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516001600160a01b031660009081526117766020908152604080832082850180518552908352818420849055858452611777909252822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560018101839055600281018390556003810183905560048101839055600581018390556006810180547fffffffff000000000000000000000000000000000000000000000000000000001690556007019190915581519051610f5e9190613128565b610f758160000151826020015183604001516131ab565b60405182907f14b9c40404d5b41deb481f9a40b8aeb2bf4b47679b38cf757075a66ed510f7f190600090a250610fac6001610b8755565b50565b610fb76130b3565b6001600160a01b038083166000908152611f4e6020908152604080832085845290915281205490911690610fe9612fd5565b90506001600160a01b03821661102b576040517fc09f8e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001600160a01b0316826001600160a01b031614611081576040517ff049b41a0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610a58565b6001600160a01b0384166000908152611f4e602090815260408083208684529091528120556110b18484836131ab565b60405183906001600160a01b038616907f70c7877531c04c7d9caa8a7eca127384f04e8a6ee58b63f778ce5401d8bcae4190600090a350506110f46001610b8755565b5050565b6001600160a01b0382166000908152612337602090815260408083208484529091528120805463ffffffff1642111561113557600091505061117e565b8054600182015470010000000000000000000000000000000090910460201b73ffffffffffffffffffffffffffffffff0000000016600160a01b90910463ffffffff16175b9150505b92915050565b61118c6130b3565b6001600160a01b0384166000908152612337602090815260408083208684529091529020805463ffffffff164211156111fc5780546040517f8c9e57cf00000000000000000000000000000000000000000000000000000000815263ffffffff9091166004820152602401610a58565b805464010000000090046bffffffffffffffffffffffff168211156112675780546040517f242373610000000000000000000000000000000000000000000000000000000081526401000000009091046bffffffffffffffffffffffff166004820152602401610a58565b60018101546001600160a01b038481169116146112c15760018101546040517fa7d95dc30000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b6112cb85856131b6565b506112d76001610b8755565b50505050565b600082815261119a60205260408120546001600160a01b0316801561134157600084815261119b602090815260408083206001600160a01b038716845290915290205460ff168061117a5750806001600160a01b0316836001600160a01b03161491505b5092915050565b600081815261119a6020908152604080832081516060808201845282546001600160a01b0381168352600160a01b900461ffff169482019490945260018201805494959485948594929084019161139e90615ab4565b80601f01602080910402602001604051908101604052809291908181526020018280546113ca90615ab4565b80156114175780601f106113ec57610100808354040283529160200191611417565b820191906000526020600020905b8154815290600101906020018083116113fa57829003601f168201915b50505091909252505050604081015181516020909201519097919650945092505050565b60008282808303611478576040517fe808160000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113888661ffff1611156114b8576040517f2b7b866100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611199805460010190819055925060006114d0612fd5565b90506040518060600160405280826001600160a01b031681526020018861ffff1681526020018a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525093909452505086815261119a6020908152604091829020845181549286015161ffff16600160a01b027fffffffffffffffffffff000000000000000000000000000000000000000000009093166001600160a01b039091161791909117815590830151909150600182019061159d9082615b4d565b50905050806001600160a01b0316847f9eee3ce0e6f7eeabd69ecf363898e9f490dbfda9ad953e1019a2c6aeceb4a7ef8b8b8b6040516115df93929190615c0d565b60405180910390a36115f28487876134b1565b50505095945050505050565b6001600160a01b038083166000908152611f4e6020908152604080832085845290915281205490911690816116365750600019610d41565b506001600160a01b03929092166000908152611f4e6020908152604080832093835292905220549091600160a01b9091046bffffffffffffffffffffffff1690565b6000818152611777602052604081206005810154820361169c576007015492915050565b6116a9816007015461362d565b9392505050565b6040517f40c1a064000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b038416906340c1a06490619c40906024016020604051808303818786fa158015611715573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116a99190615c4a565b6112d783838360006127a6565b60006116a9838361364f565b600061176085858561365b565b1561176d57506000611af7565b61177785856136ce565b156117ae576040517f83a483f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0385166000908152612337602090815260408083208784529091528120906117db612fd5565b825490915063ffffffff164211156118a2576040517f4ec58ed70000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152602482018790527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504431690634ec58ed790349060440160206040518083038185885af1158015611876573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061189b9190615c67565b92506119f0565b81546000906118c69064010000000090046bffffffffffffffffffffffff1661362d565b905080861015611905576040517fe40a30e600000000000000000000000000000000000000000000000000000000815260048101829052602401610a58565b600183015483546040517f5fdec5610000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015263ffffffff821660248201526401000000009091046bffffffffffffffffffffffff1660448201528382166064820152608481018890527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d5044390911690635fdec56190349060a40160206040518083038185885af11580156119c7573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906119ec9190615c67565b9350505b600182018054835463ffffffff8681167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909216919091176401000000006bffffffffffffffffffffffff8a1602176fffffffffffffffffffffffffffffffff908116602089811c909216700100000000000000000000000000000000021786556001600160a01b038581167fffffffffffffffff0000000000000000000000000000000000000000000000009094168417600160a01b938a1693909302929092179093556040805189815293840187905291928992918b16917ece0a712e4e277ac7b34942865f0de7a5629dffe0539b70423ad5ff1ed6ab42910160405180910390a450505b949350505050565b600083815261119a602052604090205483906001600160a01b0316611b22612fd5565b6001600160a01b0316816001600160a01b031614611bb7576001600160a01b038116611b7a576040517f0c77a95c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fb39cb29b0000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610a58565b83836000819003611bf4576040517fe808160000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bff8787876134b1565b50505050505050565b611c106130b3565b600081815261177760205260408120600501549003611c5b576040517f4b6ad8fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c6681600061371a565b610fac6001610b8755565b611c796130b3565b611c848383836139bb565b611e81576bffffffffffffffffffffffff811115611cce576040517f35ec82cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038381166000908152611f4e60209081526040808320868452909152902080549091811690600160a01b90046bffffffffffffffffffffffff1683148015611d2557506001600160a01b03811615155b15611d5c576040517fb6950f3600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546001600160a01b0316600160a01b6bffffffffffffffffffffffff8516021782556000611d89612fd5565b90506001600160a01b038216611dd957611da38686613a23565b82547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038216178355611e2f565b806001600160a01b0316826001600160a01b031614611e2f576040517f697d918e0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610a58565b806001600160a01b031685876001600160a01b03167ffcc77ea8bdcce862f43b7fb00fe6b0eb90d6aeead27d3800d9257cf7a05f9d9687604051611e7591815260200190565b60405180910390a45050505b611e8c6001610b8755565b505050565b600081815261119a602052604090205481906001600160a01b0316611eb4612fd5565b6001600160a01b0316816001600160a01b031614611f0c576001600160a01b038116611b7a576040517f0c77a95c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815261119a6020526040812080547fffffffffffffffffffff0000000000000000000000000000000000000000000016815590611f4f600183018261538d565b505060405183907f2a9aeaf340ca0da469c1f7e3d513c0e6c9cd287016f29d257a4ef70e13dc441c90600090a2505050565b600054610100900460ff1615808015611fa15750600054600160ff909116105b80611fbb5750303b158015611fbb575060005460ff166001145b612047576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a58565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156120a557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6120b0600161138d55565b8015610fac57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b610fac81346000612400565b61218460405180610100016040528060006001600160a01b031681526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600081525090565b506000908152611777602090815260409182902082516101008101845281546001600160a01b03908116825260018301549382019390935260028201548316938101939093527f000000000000000000000000000000000000000000000000000000000001518060608401526103846080840152600581015460a0840152600681015490911660c08301526007015460e082015290565b611e8c8383836000612320565b6001600160a01b038216600090815261233760209081526040808320848452909152812080548291829163ffffffff16421115612270576000806000935093509350506122a6565b600181015490546001600160a01b03909116935063ffffffff8116925064010000000090046bffffffffffffffffffffffff1690505b9250925092565b6000806060806000806122c08989613a2d565b90506122d18989838a600080613acb565b5093995091965094509250600090505b8351811015612313578381815181106122fc576122fc6158fd565b6020026020010151860195508060010190506122e1565b5093975093979195509350565b6001600160a01b0384166000908152611f4e6020908152604080832086845290915290208054600160a01b90046bffffffffffffffffffffffff168310156123ad5780546040517f16b5016f000000000000000000000000000000000000000000000000000000008152600160a01b9091046bffffffffffffffffffffffff166004820152602401610a58565b80546001600160a01b03166123ee576040517fda48e18400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123f9858584614132565b5050505050565b6124086130b3565b6000838152611777602052604081206007810154909103612455576040517f125197d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3483101561248f576040517fe2bbc1e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005810154600061249e612fd5565b90506001600160a01b0384161515806124b657508115155b15612529576002830180546001600160a01b0316604086901c6bffffffffffffffffffffffff16600160a01b908102919091179091556006840180547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1667ffffffffffffffff87169092029190911790555b816000036125fe5782600701548510156125775782600701546040517f31e6f71c000000000000000000000000000000000000000000000000000000008152600401610a5891815260200190565b82546001840154612591916001600160a01b031690614271565b600783018590556006830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383161790557f00000000000000000000000000000000000000000000000000000000000151804201600584018190559150612747565b61260782421190565b15612641576040517f3feeb88d00000000000000000000000000000000000000000000000000000000815260048101839052602401610a58565b60068301546001600160a01b0380831691160361268a576040517fe140576800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612699846007015461362d565b9050808610156126d8576040517fcd698a1900000000000000000000000000000000000000000000000000000000815260048101829052602401610a58565b50600783018054600685018054928890556001600160a01b038481167fffffffffffffffffffffffff000000000000000000000000000000000000000085161790915590911642610384018085101561273657600586018190559350835b506127448183614e2061427b565b50505b6127528560006143b7565b60408051868152602081018490526001600160a01b0383169188917f26ea3ebbda62eb1baef13e1c237dddd956c87f80b2801f2616d806d52557b121910160405180910390a3505050611e8c6001610b8755565b60006127b06130b3565b82806000036127eb576040517f3a970fe600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61138d80546001810190915591506128038686613a23565b6001600160a01b03861660009081526117766020908152604080832088845290915290205415612889576001600160a01b038616600090815261177660209081526040808320888452909152908190205490517f7618a0030000000000000000000000000000000000000000000000000000000081526004810191909152602401610a58565b6000612893612fd5565b6001600160a01b038881166000818152611776602090815260408083208c84528252808320899055888352611777909152902080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081169092178155600181018a90556002810180549092169284169290921790556007810187905590915061291f8888876144a2565b604080517f0000000000000000000000000000000000000000000000000000000000015180815261038460208201529081018790526060810185905287906001600160a01b03808b1691908516907f1062dd3b35f12b4064331244d00f40c1d4831965e4285654157a2409c6217cff9060800160405180910390a4505050611af76001610b8755565b6001600160a01b0382166000908152612337602090815260408083208484529091528120805463ffffffff164211612a035780546129fb9064010000000090046bffffffffffffffffffffffff1661362d565b91505061117e565b5060019392505050565b6040517fde5488af0000000000000000000000000000000000000000000000000000000081526001600160a01b03848116600483015260609182917f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d169063de5488af90619c40906024016020604051808303818786fa93505050508015612ab2575060408051601f3d908101601f19168201909252612aaf91810190615c4a565b60015b15612d0357856001600160a01b0316816001600160a01b031614612d0157945084612b066001600160a01b0382167f2a55205a00000000000000000000000000000000000000000000000000000000612fe4565b15612c11576040517f2a55205a0000000000000000000000000000000000000000000000000000000081526004810186905261271060248201526001600160a01b03871690632a55205a90619c409060440160408051808303818786fa93505050508015612b91575060408051601f3d908101601f19168201909252612b8e918101906158a0565b60015b15612c11578015612c0e5760408051600180825281830190925290602080830190803683370190505094508185600081518110612bd057612bd06158fd565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337019050509350505050612fcd565b50505b8251158015612c4e5750612c4e6001600160a01b0387167fbb3bafd600000000000000000000000000000000000000000000000000000000612fe4565b15612d01576040517fbb3bafd6000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b0387169063bb3bafd690619c40906024016000604051808303818786fa93505050508015612cd757506040513d6000823e601f3d908101601f19168201604052612cd49190810190615a50565b60015b15612d0157815115801590612ced575080518251145b15612cfe579093509150612fcd9050565b50505b505b612d366001600160a01b0386167fb779958400000000000000000000000000000000000000000000000000000000612fe4565b15612e6b576040517fb9c4d9fb000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b0386169063b9c4d9fb90619c40906024016000604051808303818786fa93505050508015612dbf57506040513d6000823e601f3d908101601f19168201604052612dbc9190810190615c80565b60015b15612e6b57805115612e69576040517f0ebd4c7f000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03871690630ebd4c7f90619c40906024016000604051808303818786fa93505050508015612e4f57506040513d6000823e601f3d908101601f19168201604052612e4c9190810190615cb5565b60015b15612e69578051825103612e67579092509050612fcd565b505b505b6001600160a01b03831615612ee95760408051600180825281830190925290602080830190803683370190505091508282600081518110612eae57612eae6158fd565b6001600160a01b0392909216602092830291909101820152604080516001808252818301909252918281019080368337019050509050612fcd565b846001600160a01b0316638da5cb5b619c406040518263ffffffff1660e01b81526004016020604051808303818786fa93505050508015612f47575060408051601f3d908101601f19168201909252612f4491810190615c4a565b60015b15612fcd576001600160a01b03811615612fcb5760408051600180825281830190925290602080830190803683370190505092508083600081518110612f8f57612f8f6158fd565b6001600160a01b039290921660209283029190910182015260408051600180825281830190925291828101908036833701905050915050612fcd565b505b935093915050565b6000612fdf6145d7565b905090565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d9150600051905082801561309c575060208210155b80156130a85750600081115b979650505050505050565b6002610b875403613120576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a58565b6002610b8755565b6001600160a01b038216600090815261119c602090815260408083208484529091529020548015611e8c576001600160a01b038316600081815261119c60209081526040808320868452909152808220829055518392859290917f2ea2946ee16c4a1d0ec58464194022e54432a6d7db359835ddf283555f2c8eee9190a4505050565b611e8c838383614634565b6001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046bffffffffffffffffffffffff9081168489019081527001000000000000000000000000000000009093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c5297909952989094557fffffffffffffffff00000000000000000000000000000000000000000000000090961690965591518251955193517f4dc8fb3c000000000000000000000000000000000000000000000000000000008152908716600482015294909316602485015291166044830152917f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504431690634dc8fb3c90606401600060405180830381600087803b15801561331357600080fd5b505af1158015613327573d6000803e3d6000fd5b505050506000613335612fd5565b60608301516040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b0380841660048301529182166024820152604481018690529192508516906323b872dd90606401600060405180830381600087803b1580156133a857600080fd5b505af19250505080156133b9575060015b6133cd576133cd848484606001518461466b565b600080600061342b87878688602001516bffffffffffffffffffffffff166134238a604001518b6080015173ffffffffffffffffffffffffffffffff0000000060209290921b9190911663ffffffff9091161790565b600080614677565b92509250925084606001516001600160a01b031686886001600160a01b03167f1cb8adb37d6d35e94cd0695ca39895b84371864713f5ca7eada52af9ff23744b878787876040516134a094939291906001600160a01b0394909416845260208401929092526040830152606082015260800190565b60405180910390a450505050505050565b60005b818110156135ed5760008383838181106134d0576134d06158fd565b90506020020160208101906134e59190615cea565b600086815261119b602090815260408083206001600160a01b038516845290915290205490915060ff1615613546576040517f667888ec00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61354e612fd5565b6001600160a01b0316816001600160a01b031603613598576040517f43e2197f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815261119b602090815260408083206001600160a01b0390941683529290522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155016134b4565b50827fd5a31bd2d34d303249ac7f54bfc7578390f90f5d39cb39813f67190fa36f5c178383604051613620929190615d07565b60405180910390a2505050565b6000600a8204808203613645576116a9836001615d79565b6116a98382615d79565b60006116a9838361489e565b6001600160a01b038084166000908152611f4e6020908152604080832086845290915281208054919290911615806136a857508054600160a01b90046bffffffffffffffffffffffff1683105b156136b75760009150506116a9565b6136c385856000614132565b506001949350505050565b6001600160a01b038216600090815261177660209081526040808320848452909152812054801580159061117a5750600090815261177760205260409020600501544211159392505050565b6000828152611777602090815260409182902082516101408101845281546001600160a01b039081168252600183015493820193909352600282015480841694820194909452600160a01b938490046bffffffffffffffffffffffff16606082015260038201546080820152600482015460a0820152600582015460c08201819052600683015493841660e08301529390920467ffffffffffffffff1661010083015260070154610120820152904211613808578060c001516040517f3a017f60000000000000000000000000000000000000000000000000000000008152600401610a5891815260200190565b60008061381d836000015184602001516148d4565b84516001600160a01b03166000908152611776602090815260408083208289015184528252808320839055898352611777909152812080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560018101829055600281018290556003810182905560048101829055600581018290556006810180547fffffffff00000000000000000000000000000000000000000000000000000000169055600701559092509050836138ed576138ed836000015184602001518560e001516000614950565b60008060006139428660000151876020015188604001518961012001518a610100015167ffffffffffffffff1660408c606001516bffffffffffffffffffffffff166001600160a01b0316901b178a8a614677565b9250925092508560e001516001600160a01b031686604001516001600160a01b0316897f2edb0e99c6ac35be6731dab554c1d1fa1b7beb675090dbb09fb14e615aca1c4a8686866040516139a9939291909283526020830191909152604082015260600190565b60405180910390a45050505050505050565b6001600160a01b0383166000908152612337602090815260408083208584529091528120805463ffffffff16421180613a0a5750805464010000000090046bffffffffffffffffffffffff1683115b15613a195760009150506116a9565b6136c385856131b6565b6110f48282614a18565b6000613a39838361364f565b90506001600160a01b03811661117e576040517f6352211e000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015613aa7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a99190615c4a565b6000606080828080612710613b007f00000000000000000000000000000000000000000000000000000000000001f48b615d8c565b613b0a9190615da3565b6040517f4c542f770000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e90529197506000917f0000000000000000000000003dcea4703af986ebb55ba37af1729fe56182f87d1690634c542f7790604401602060405180830381865afa925050508015613bb2575060408051601f3d908101601f19168201909252613baf91810190615c4a565b60015b15613bba5790505b6040517f0d7daf3e0000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e90527f0000000000000000000000003dcea4703af986ebb55ba37af1729fe56182f87d1690630d7daf3e90604401600060405180830381865afa925050508015613c6157506040513d6000823e601f3d908101601f19168201604052613c5e9190810190615a50565b60015b15613c6c5790965094505b8551600003613d2f576040517fefef76f80000000000000000000000000000000000000000000000000000000081526001600160a01b038e81166004830152602482018e905282811660448301527f0000000000000000000000003dcea4703af986ebb55ba37af1729fe56182f87d169063efef76f890606401600060405180830381865afa925050508015613d2457506040513d6000823e601f3d908101601f19168201604052613d219190810190615a50565b60015b15613d2f5790965094505b8551151580613d5b57507f00000000000000000000000000000000000000000000000000000000000000005b1561406b5760007f000000000000000000000000000000000000000000000000000000000000000015613e0657878b0390508651600003613e015760408051600180825281830190925290602080830190803683370190505096508b87600081518110613dca57613dca6158fd565b6001600160a01b03929092166020928302919091018201526040805160018082528183019092529182810190803683370190505095505b613e87565b816001600160a01b03168c6001600160a01b03161480613e5c5750865115801590613e5c575086600081518110613e3f57613e3f6158fd565b60200260200101516001600160a01b03168c6001600160a01b0316145b15613e6a5750868a03613e87565b50600a8a0480613e7a898d615dde565b613e849190615dde565b94505b613e92876005614aab565b613e9d866005614aab565b61ffff891615613eea57612710613eb861ffff8b168d615d8c565b613ec29190615da3565b925084600003613edd57613ed68382615dde565b9050613eea565b613ee78386615dde565b94505b6000600188511115613fc45760005b8851811015613f9a578d6001600160a01b0316898281518110613f1e57613f1e6158fd565b60200260200101516001600160a01b031603613f3d5760009692909201915b6000198214613f9257612710888281518110613f5b57613f5b6158fd565b60200260200101511115613f73576000199150613f92565b878181518110613f8557613f856158fd565b6020026020010151820191505b600101613ef9565b50801580613fa9575060001981145b15613fc457613fb9886001614aab565b613fc4876001614aab565b600060015b8951811015614039576000838a8381518110613fe757613fe76158fd565b602002602001015186613ffa9190615d8c565b6140049190615da3565b90506140108184615d79565b9250808a8381518110614025576140256158fd565b602090810291909101015250600101613fc9565b506140448184615dde565b88600081518110614057576140576158fd565b6020026020010181815250505050506140a4565b868a03935061ffff8816156140a45761271061408b61ffff8a168c615d8c565b6140959190615da3565b91506140a18285615dde565b93505b6001600160a01b038916158015906140d557506140bf612fd5565b6001600160a01b0316896001600160a01b031614155b80156140f357508a6001600160a01b0316896001600160a01b031614155b80156141115750806001600160a01b0316896001600160a01b031614155b156141225760648a04925082870396505b5096509650965096509650969050565b61413a6130b3565b6001600160a01b038381166000908152611f4e60209081526040808320868452808352818420825180840190935280549586168352600160a01b9095046bffffffffffffffffffffffff1682840152868452909152915561419b8484614ab8565b6141b881602001516bffffffffffffffffffffffff1660016143b7565b60006141c2612fd5565b90506141d1858583600061466b565b60008060006141fc8888876000015188602001516bffffffffffffffffffffffff168a600080614677565b8751604080516001600160a01b038a81168252602082018790529181018590526060810184905294975092955090935081169189918b16907fd28c0a7dd63bc853a4e36306655da9f8c0b29ff9d0605bb976ae420e46a999309060800160405180910390a45050505050611e8c6001610b8755565b6110f48282614b05565b8160000361428857505050565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d80600081146142d8576040519150601f19603f3d011682016040523d82523d6000602084013e6142dd565b606091505b50509050806112d7576040517faa67c9190000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063aa67c9199085906024016000604051808303818588803b15801561436357600080fd5b505af1158015614377573d6000803e3d6000fd5b5050505050836001600160a01b03167fa2201512569adb2d513531dfd69b66df50bd5cffb8c1bbe65a4611f9e1eadbd184604051610b0f91815260200190565b34821115614474577f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436001600160a01b031663452f2b8f6143f6612fd5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b03909116600482015234850360248201526044015b600060405180830381600087803b15801561445857600080fd5b505af115801561446c573d6000803e3d6000fd5b505050505050565b80801561448057503482105b156110f4576110f4823403614493612fd5565b6001600160a01b031690614b19565b8015611e8c57600081815261119a6020526040902080546001600160a01b03166144f8576040517f0c77a95c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000614502612fd5565b600084815261119b602090815260408083206001600160a01b038516845290915290205490915060ff16158015614546575081546001600160a01b03828116911614155b1561457d576040517f6e93a35400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516600081815261119c60209081526040808320888452909152808220869055518592879290917fb17e0c916df75a12480835f00b3927cb871bbe00bacf819f81a1d92f9ff7f38d9190a45050505050565b336001600160a01b037f000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e316810361463157507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec36013560601c5b90565b6001600160a01b038084166000908152611f4e6020908152604080832086845290915290205416806112d7576112d7848484614c66565b6112d784848484614c9c565b60008060008660000361469257506000915081905080614891565b6060806000806146a68e8e8e8e8e8d613acb565b8451959c5091995092975090955090935091506000906001146146cb57614e206146d0565b620334505b905060005b85518110156147445761471b8682815181106146f3576146f36158fd565b602002602001015186838151811061470d5761470d6158fd565b60200260200101518461427b565b84818151811061472d5761472d6158fd565b6020026020010151880197508060010190506146d5565b506147528d87614e2061427b565b61477f7f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb689614e2061427b565b82156147fa576147928b84614e2061427b565b8d8f6001600160a01b03167f141b92fd9766c80ab120598ea2f6be9802470ec59b5446dd9bf46214ead8d08e8d8660006040516147ed939291906001600160a01b039390931683526020830191909152604082015260600190565b60405180910390a3968201965b6001600160a01b038a161561488b57811561483257856000036148205795810195614825565b948101945b6148328a83614e2061427b565b8d8f6001600160a01b03167f27a4dd4ff659a9e6354fb079b2208365e5b83f55c22a4150eee2bca89501cb988c856040516148829291906001600160a01b03929092168252602082015260400190565b60405180910390a35b50505050505b9750975097945050505050565b6001600160a01b038083166000908152611f4e60209081526040808320858452909152902054168061117e576116a98383614d39565b6001600160a01b038216600090815261119c602090815260408083208484529091528120548190801561494857600081815261119a60209081526040808320546001600160a01b03898116855261119c84528285208986529093529083209290925581169350600160a01b900461ffff1691505b509250929050565b6001600160a01b03811615614991576040517f57a016b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152604482018590528516906323b872dd90606401600060405180830381600087803b1580156149fa57600080fd5b505af1158015614a0e573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680614a4e57611e8c8383614d7a565b614a56612fd5565b6001600160a01b0316816001600160a01b031614611e8c576040517f32f3b0330000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610a58565b80825111156110f4579052565b6001600160a01b0382166000908152612337602090815260408083208484529091529020614ae4612fd5565b60018201546001600160a01b03918216911603611e8c57611e8c8383614e9b565b614b0f8282614e9b565b6110f4828261507c565b80471015614b83576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610a58565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614bd0576040519150601f19603f3d011682016040523d82523d6000602084013e614bd5565b606091505b5050905080611e8c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610a58565b6001600160a01b0383166000908152611776602090815260408083208584529091528120549003611e8c57611e8c8383836150b5565b6001600160a01b038085166000908152611f4e60209081526040808320878452909152902054168015614d2d57816001600160a01b0316816001600160a01b031614614d1f576040517f32f3b0330000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610a58565b60009150614d2d85856150c2565b6123f985858585615115565b6001600160a01b038083166000908152611776602090815260408083208584528252808320548352611777909152902060020154168061117e5760006116a9565b6001600160a01b03821660009081526117766020908152604080832084845290915281205490819003614db157611e8c8383615325565b60008181526117776020526040812090614dc9612fd5565b90508160050154600003614e365760028201546001600160a01b03828116911614614e315760028201546040517fe64526ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b6123f9565b60068201546001600160a01b03828116911614614e905760068201546040517fe64526ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b6123f983600161371a565b6001600160a01b03821660009081526123376020908152604080832084845290915290205463ffffffff1642116110f4576001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046bffffffffffffffffffffffff9081168489019081527001000000000000000000000000000000009093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c5297909952989094557fffffffffffffffff00000000000000000000000000000000000000000000000090961690965591518251955193517f345db493000000000000000000000000000000000000000000000000000000008152908716600482015294909316602485015291166044830152917f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063345db49390606401600060405180830381600087803b15801561502957600080fd5b505af115801561503d573d6000803e3d6000fd5b50506040518492506001600160a01b03861691507f30c264456cbd17f5f67d7534654161414f34c0e6cc1b7500e169b7a7aea4afc090600090a3505050565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020805490911615611e8c57611e8c83836150c2565b611e8c838383600061466b565b6001600160a01b0382166000818152611f4e60209081526040808320858452909152808220829055518392917faa6271d89a385571e237d3e7254ccc7c09f68055e6e9b410ed08233a8b9a05cf91a35050565b6001600160a01b03841660009081526117766020908152604080832086845290915290205480156153195760008181526117776020526040812060058101549091036152ae576001600160a01b03831615801590615183575060028101546001600160a01b03848116911614155b156151cb5760028101546040517fe64526ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b6001600160a01b0386166000908152611776602090815260408083208884528252808320839055848352611777909152812080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560018101829055600281018290556003810182905560048101829055600581018290556006810180547fffffffff000000000000000000000000000000000000000000000000000000001690556007015561527e8686613128565b60405182907f5603897cc9b1e866f3f7395ffc6638776041f21c094d0b4e748ff44c407fa36290600090a2615313565b60068101546001600160a01b038481169116146153085760068101546040517fe64526ee0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610a58565b61531382600161371a565b60009250505b6123f985858585614950565b816001600160a01b03166323b872dd61533c612fd5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b0390911660048201523060248201526044810184905260640161443e565b50805461539990615ab4565b6000825580601f106153a9575050565b601f016020900490600052602060002090810190610fac91905b808211156153d757600081556001016153c3565b5090565b600080604083850312156153ee57600080fd5b50508035926020909101359150565b6001600160a01b0381168114610fac57600080fd5b6000806040838503121561542557600080fd5b8235615430816153fd565b946020939093013593505050565b600081518084526020808501945080840160005b838110156154775781516001600160a01b031687529582019590820190600101615452565b509495945050505050565b600081518084526020808501945080840160005b8381101561547757815187529582019590820190600101615496565b6040815260006154c5604083018561543e565b82810360208401526154d78185615482565b95945050505050565b6000602082840312156154f257600080fd5b5035919050565b6000806000806080858703121561550f57600080fd5b843561551a816153fd565b9350602085013592506040850135615531816153fd565b9396929550929360600135925050565b6000806040838503121561555457600080fd5b823591506020830135615566816153fd565b809150509250929050565b606081526000845180606084015260005b8181101561559f5760208188018101516080868401015201615582565b506000608082850101526080601f19601f8301168401019150506001600160a01b038416602083015261ffff83166040830152949350505050565b60008083601f8401126155ec57600080fd5b50813567ffffffffffffffff81111561560457600080fd5b6020830191508360208260051b8501011115610d4157600080fd5b60008060008060006060868803121561563757600080fd5b853567ffffffffffffffff8082111561564f57600080fd5b818801915088601f83011261566357600080fd5b81358181111561567257600080fd5b89602082850101111561568457600080fd5b602092830197509550908701359061ffff821682146156a257600080fd5b909350604087013590808211156156b857600080fd5b506156c5888289016155da565b969995985093965092949392505050565b6000806000606084860312156156eb57600080fd5b83356156f6816153fd565b95602085013595506040909401359392505050565b6000806000806080858703121561572157600080fd5b843561572c816153fd565b93506020850135925060408501359150606085013561574a816153fd565b939692955090935050565b60008060006040848603121561576a57600080fd5b83359250602084013567ffffffffffffffff81111561578857600080fd5b615794868287016155da565b9497909650939450505050565b86815285602082015260c0604082015260006157c060c083018761543e565b82810360608401526157d28187615482565b9150508360808301526001600160a01b03831660a0830152979650505050505050565b60008060006060848603121561580a57600080fd5b83359250602084013591506040840135615823816153fd565b809150509250925092565b6000806000806080858703121561584457600080fd5b843561584f816153fd565b966020860135965060408601359560600135945092505050565b60008060006060848603121561587e57600080fd5b8335615889816153fd565b9250602084013591506040840135615823816153fd565b600080604083850312156158b357600080fd5b82516158be816153fd565b6020939093015192949293505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715615955576159556158ce565b604052919050565b600067ffffffffffffffff821115615977576159776158ce565b5060051b60200190565b600082601f83011261599257600080fd5b815160206159a76159a28361595d565b61592c565b82815260059290921b840181019181810190868411156159c657600080fd5b8286015b848110156159ea5780516159dd816153fd565b83529183019183016159ca565b509695505050505050565b600082601f830112615a0657600080fd5b81516020615a166159a28361595d565b82815260059290921b84018101918181019086841115615a3557600080fd5b8286015b848110156159ea5780518352918301918301615a39565b60008060408385031215615a6357600080fd5b825167ffffffffffffffff80821115615a7b57600080fd5b615a8786838701615981565b93506020850151915080821115615a9d57600080fd5b50615aaa858286016159f5565b9150509250929050565b600181811c90821680615ac857607f821691505b602082108103615b01577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f821115611e8c57600081815260208120601f850160051c81016020861015615b2e5750805b601f850160051c820191505b8181101561446c57828155600101615b3a565b815167ffffffffffffffff811115615b6757615b676158ce565b615b7b81615b758454615ab4565b84615b07565b602080601f831160018114615bb05760008415615b985750858301515b600019600386901b1c1916600185901b17855561446c565b600085815260208120601f198616915b82811015615bdf57888601518255948401946001909101908401615bc0565b5085821015615bfd5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081528260408201528284606083013760006060848301015260006060601f19601f860116830101905061ffff83166020830152949350505050565b600060208284031215615c5c57600080fd5b81516116a9816153fd565b600060208284031215615c7957600080fd5b5051919050565b600060208284031215615c9257600080fd5b815167ffffffffffffffff811115615ca957600080fd5b61117a84828501615981565b600060208284031215615cc757600080fd5b815167ffffffffffffffff811115615cde57600080fd5b61117a848285016159f5565b600060208284031215615cfc57600080fd5b81356116a9816153fd565b60208082528181018390526000908460408401835b868110156159ea578235615d2f816153fd565b6001600160a01b031682529183019190830190600101615d1c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561117e5761117e615d4a565b808202811582820484141761117e5761117e615d4a565b600082615dd9577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b8181038181111561117e5761117e615d4a56fea264697066735822122077d50ffac51a93cc0246c53b240f3653f9282905120beac5a5e9e9e2ca93892964736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb600000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d0000000000000000000000000000000000000000000000000000000000015180000000000000000000000000762340b8a40cdd5bfc3edd94265899fda345d0e3
-----Decoded View---------------
Arg [0] : treasury (address): 0x67Df244584b67E8C51B10aD610aAfFa9a402FdB6
Arg [1] : feth (address): 0x49128CF8ABE9071ee24540a296b5DED3F9D50443
Arg [2] : royaltyRegistry (address): 0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D
Arg [3] : duration (uint256): 86400
Arg [4] : router (address): 0x762340B8a40Cdd5BFC3eDD94265899FDa345D0E3
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb6
Arg [1] : 00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443
Arg [2] : 000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d
Arg [3] : 0000000000000000000000000000000000000000000000000000000000015180
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.