Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
14718784 | 951 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
FNDNFTMarket
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 1337 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.0; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./mixins/Constants.sol"; import "./mixins/FoundationTreasuryNode.sol"; import "./mixins/NFTMarketAuction.sol"; import "./mixins/NFTMarketBuyPrice.sol"; import "./mixins/NFTMarketCore.sol"; import "./mixins/NFTMarketCreators.sol"; import "./mixins/NFTMarketFees.sol"; import "./mixins/NFTMarketOffer.sol"; import "./mixins/NFTMarketPrivateSale.sol"; import "./mixins/NFTMarketReserveAuction.sol"; import "./mixins/SendValueWithFallbackWithdraw.sol"; /** * @title A market for NFTs on Foundation. * @notice The Foundation marketplace is a contract which allows traders to buy and sell NFTs. * It supports buying and selling via auctions, private sales, buy price, and offers. * @dev All sales in the Foundation market will pay the creator 10% royalties on secondary sales. This is not specific * to NFTs minted on Foundation, it should work for any NFT. If royalty information was not defined when the NFT was * originally deployed, it may be added using the [Royalty Registry](https://royaltyregistry.xyz/) which will be * respected by our market contract. */ contract FNDNFTMarket is Constants, Initializable, FoundationTreasuryNode, NFTMarketCore, ReentrancyGuardUpgradeable, NFTMarketCreators, SendValueWithFallbackWithdraw, NFTMarketFees, NFTMarketAuction, NFTMarketReserveAuction, NFTMarketPrivateSale, NFTMarketBuyPrice, NFTMarketOffer { /** * @notice Set immutable variables for the implementation contract. * @dev Using immutable instead of constants allows us to use different values on testnet. * @param treasury The Foundation Treasury contract address. * @param feth The FETH ERC-20 token contract address. * @param royaltyRegistry The Royalty Registry contract address. * @param duration The duration of the auction in seconds. * @param marketProxyAddress The address of the proxy fronting this contract. */ constructor( address payable treasury, address feth, address royaltyRegistry, uint256 duration, address marketProxyAddress ) FoundationTreasuryNode(treasury) NFTMarketCore(feth) NFTMarketCreators(royaltyRegistry) NFTMarketReserveAuction(duration) NFTMarketPrivateSale(marketProxyAddress) // solhint-disable-next-line no-empty-blocks {} /** * @notice Called once to configure the contract after the initial proxy deployment. * @dev This farms the initialize call out to inherited contracts as needed to initialize mutable variables. */ function initialize() external initializer { NFTMarketAuction._initializeNFTMarketAuction(); } /** * @inheritdoc NFTMarketCore * @dev This is a no-op function required to avoid compile errors. */ function _beforeAuctionStarted(address nftContract, uint256 tokenId) internal override(NFTMarketCore, NFTMarketBuyPrice, NFTMarketOffer) { super._beforeAuctionStarted(nftContract, tokenId); } /** * @inheritdoc NFTMarketCore * @dev This is a no-op function required to avoid compile errors. */ function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address authorizeSeller ) internal override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) { super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller); } /** * @inheritdoc NFTMarketCore * @dev This is a no-op function required to avoid compile errors. */ function _transferFromEscrowIfAvailable( address nftContract, uint256 tokenId, address recipient ) internal override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) { super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient); } /** * @inheritdoc NFTMarketCore * @dev This is a no-op function required to avoid compile errors. */ function _transferToEscrow(address nftContract, uint256 tokenId) internal override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) { super._transferToEscrow(nftContract, tokenId); } /** * @inheritdoc NFTMarketCore * @dev This is a no-op function required to avoid compile errors. */ function _getSellerFor(address nftContract, uint256 tokenId) internal view override(NFTMarketCore, NFTMarketReserveAuction, NFTMarketBuyPrice) returns (address payable seller) { seller = super._getSellerFor(nftContract, tokenId); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.0; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() initializer {} * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { // If the contract is initializing we ignore whether _initialized is set in order to support multiple // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the // contract may have been reentered. require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} modifier, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; /** * @title Constant values shared across mixins. */ abstract contract Constants { /** * @notice 100% in basis points. */ uint256 internal constant BASIS_POINTS = 10000; /** * @notice Cap the number of royalty recipients. * @dev A cap is required to ensure gas costs are not too high when a sale is settled. */ uint256 internal constant MAX_ROYALTY_RECIPIENTS = 5; /** * @notice The minimum increase of 10% required when making an offer or placing a bid. */ uint256 internal constant MIN_PERCENT_INCREMENT_DENOMINATOR = BASIS_POINTS / 1000; /** * @notice The gas limit used when making external read-only calls. * @dev This helps to ensure that external calls does not prevent the market from executing. */ uint256 internal constant READ_ONLY_GAS_LIMIT = 40000; /** * @notice The gas limit to send ETH to multiple recipients, enough for a 5-way split. */ uint256 internal constant SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS = 210000; /** * @notice The gas limit to send ETH to a single recipient, enough for a contract with a simple receiver. */ uint256 internal constant SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT = 20000; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../interfaces/IAdminRole.sol"; import "../interfaces/IOperatorRole.sol"; error FoundationTreasuryNode_Address_Is_Not_A_Contract(); error FoundationTreasuryNode_Caller_Not_Admin(); error FoundationTreasuryNode_Caller_Not_Operator(); /** * @title A mixin that stores a reference to the Foundation treasury contract. * @notice The treasury collects fees and defines admin/operator roles. */ abstract contract FoundationTreasuryNode is Initializable { using AddressUpgradeable for address payable; /// @dev This value was replaced with an immutable version. address payable private __gap_was_treasury; /// @notice The address of the treasury contract. address payable private immutable treasury; /// @notice Requires the caller is a Foundation admin. modifier onlyFoundationAdmin() { if (!IAdminRole(treasury).isAdmin(msg.sender)) { revert FoundationTreasuryNode_Caller_Not_Admin(); } _; } /// @notice Requires the caller is a Foundation operator. modifier onlyFoundationOperator() { if (!IOperatorRole(treasury).isOperator(msg.sender)) { revert FoundationTreasuryNode_Caller_Not_Operator(); } _; } /** * @notice Set immutable variables for the implementation contract. * @dev Assigns the treasury contract address. */ constructor(address payable _treasury) { if (!_treasury.isContract()) { revert FoundationTreasuryNode_Address_Is_Not_A_Contract(); } treasury = _treasury; } /** * @notice Gets the Foundation treasury contract. * @dev This call is used in the royalty registry contract. * @return treasuryAddress The address of the Foundation treasury contract. */ function getFoundationTreasury() public view returns (address payable treasuryAddress) { return treasury; } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[2000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; /** * @title An abstraction layer for auctions. * @dev This contract can be expanded with reusable calls and data as more auction types are added. */ abstract contract NFTMarketAuction is Initializable { /** * @notice A global id for auctions of any type. */ uint256 private nextAuctionId; /** * @notice Called once to configure the contract after the initial proxy deployment. * @dev This sets the initial auction id to 1, making the first auction cheaper * and id 0 represents no auction found. */ function _initializeNFTMarketAuction() internal onlyInitializing { nextAuctionId = 1; } /** * @notice Returns id to assign to the next auction. */ function _getNextAndIncrementAuctionId() internal returns (uint256) { // AuctionId cannot overflow 256 bits. unchecked { return nextAuctionId++; } } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[1000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./NFTMarketCore.sol"; import "./NFTMarketFees.sol"; /// @param buyPrice The current buy price set for this NFT. error NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price(uint256 buyPrice); error NFTMarketBuyPrice_Cannot_Buy_Unset_Price(); error NFTMarketBuyPrice_Cannot_Cancel_Unset_Price(); /// @param owner The current owner of this NFT. error NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price(address owner); /// @param owner The current owner of this NFT. error NFTMarketBuyPrice_Only_Owner_Can_Set_Price(address owner); error NFTMarketBuyPrice_Price_Already_Set(); error NFTMarketBuyPrice_Price_Too_High(); /// @param seller The current owner of this NFT. error NFTMarketBuyPrice_Seller_Mismatch(address seller); /** * @title Allows sellers to set a buy price of their NFTs that may be accepted and instantly transferred to the buyer. * @notice NFTs with a buy price set are escrowed in the market contract. */ abstract contract NFTMarketBuyPrice is NFTMarketCore, NFTMarketFees { using AddressUpgradeable for address payable; /// @notice Stores the buy price details for a specific NFT. /// @dev The struct is packed into a single slot to optimize gas. struct BuyPrice { /// @notice The current owner of this NFT which set a buy price. /// @dev A zero price is acceptable so a non-zero address determines whether a price has been set. address payable seller; /// @notice The current buy price set for this NFT. uint96 price; } /// @notice Stores the current buy price for each NFT. mapping(address => mapping(uint256 => BuyPrice)) private nftContractToTokenIdToBuyPrice; /** * @notice Emitted when an NFT is bought by accepting the buy price, * indicating that the NFT has been transferred and revenue from the sale distributed. * @dev The total buy price that was accepted is `protocolFee` + `creatorFee` + `sellerRev`. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param buyer The address of the collector that purchased the NFT using `buy`. * @param seller The address of the seller which originally set the buy price. * @param protocolFee The amount of ETH that was sent to Foundation for this sale. * @param creatorFee The amount of ETH that was sent to the creator for this sale. * @param sellerRev The amount of ETH that was sent to the owner for this sale. */ event BuyPriceAccepted( address indexed nftContract, uint256 indexed tokenId, address indexed seller, address buyer, uint256 protocolFee, uint256 creatorFee, uint256 sellerRev ); /** * @notice Emitted when the buy price is removed by the owner of an NFT. * @dev The NFT is transferred back to the owner unless it's still escrowed for another market tool, * e.g. listed for sale in an auction. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. */ event BuyPriceCanceled(address indexed nftContract, uint256 indexed tokenId); /** * @notice Emitted when a buy price is invalidated due to other market activity. * @dev This occurs when the buy price is no longer eligible to be accepted, * e.g. when a bid is placed in an auction for this NFT. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. */ event BuyPriceInvalidated(address indexed nftContract, uint256 indexed tokenId); /** * @notice Emitted when a buy price is set by the owner of an NFT. * @dev The NFT is transferred into the market contract for escrow unless it was already escrowed, * e.g. for auction listing. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param seller The address of the NFT owner which set the buy price. * @param price The price of the NFT. */ event BuyPriceSet(address indexed nftContract, uint256 indexed tokenId, address indexed seller, uint256 price); /** * @notice Buy the NFT at the set buy price. * `msg.value` must be <= `maxPrice` and any delta will be taken from the account's available FETH balance. * @dev `maxPrice` protects the buyer in case a the price is increased but allows the transaction to continue * when the price is reduced (and any surplus funds provided are refunded). * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param maxPrice The maximum price to pay for the NFT. */ function buy( address nftContract, uint256 tokenId, uint256 maxPrice ) external payable { buyV2(nftContract, tokenId, maxPrice, payable(0)); } /** * @notice Buy the NFT at the set buy price. * `msg.value` must be <= `maxPrice` and any delta will be taken from the account's available FETH balance. * @dev `maxPrice` protects the buyer in case a the price is increased but allows the transaction to continue * when the price is reduced (and any surplus funds provided are refunded). * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param maxPrice The maximum price to pay for the NFT. * @param referrer The address of the referrer. */ function buyV2( address nftContract, uint256 tokenId, uint256 maxPrice, address payable referrer ) public payable { BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; if (buyPrice.price > maxPrice) { revert NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price(buyPrice.price); } else if (buyPrice.seller == address(0)) { revert NFTMarketBuyPrice_Cannot_Buy_Unset_Price(); } _buy(nftContract, tokenId, referrer); } /** * @notice Removes the buy price set for an NFT. * @dev The NFT is transferred back to the owner unless it's still escrowed for another market tool, * e.g. listed for sale in an auction. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. */ function cancelBuyPrice(address nftContract, uint256 tokenId) external nonReentrant { address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { // This check is redundant with the next one, but done in order to provide a more clear error message. revert NFTMarketBuyPrice_Cannot_Cancel_Unset_Price(); } else if (seller != msg.sender) { revert NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price(seller); } // Remove the buy price delete nftContractToTokenIdToBuyPrice[nftContract][tokenId]; // Transfer the NFT back to the owner if it is not listed in auction. _transferFromEscrowIfAvailable(nftContract, tokenId, msg.sender); emit BuyPriceCanceled(nftContract, tokenId); } /** * @notice Sets the buy price for an NFT and escrows it in the market contract. * A 0 price is acceptable and valid price you can set, enabling a giveaway to the first collector that calls `buy`. * @dev If there is an offer for this amount or higher, that will be accepted instead of setting a buy price. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param price The price at which someone could buy this NFT. */ function setBuyPrice( address nftContract, uint256 tokenId, uint256 price ) external nonReentrant { // If there is a valid offer at this price or higher, accept that instead. if (_autoAcceptOffer(nftContract, tokenId, price)) { return; } if (price > type(uint96).max) { // This ensures that no data is lost when storing the price as `uint96`. revert NFTMarketBuyPrice_Price_Too_High(); } BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; address seller = buyPrice.seller; if (buyPrice.price == price && seller != address(0)) { revert NFTMarketBuyPrice_Price_Already_Set(); } // Store the new price for this NFT. buyPrice.price = uint96(price); if (seller == address(0)) { // Transfer the NFT into escrow, if it's already in escrow confirm the `msg.sender` is the owner. _transferToEscrow(nftContract, tokenId); // The price was not previously set for this NFT, store the seller. buyPrice.seller = payable(msg.sender); } else if (seller != msg.sender) { // Buy price was previously set by a different user revert NFTMarketBuyPrice_Only_Owner_Can_Set_Price(seller); } emit BuyPriceSet(nftContract, tokenId, msg.sender, price); } /** * @notice If there is a buy price at this price or lower, accept that and return true. */ function _autoAcceptBuyPrice( address nftContract, uint256 tokenId, uint256 maxPrice ) internal override returns (bool) { BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; if (buyPrice.seller == address(0) || buyPrice.price > maxPrice) { // No buy price was found, or the price is too high. return false; } _buy(nftContract, tokenId, payable(0)); return true; } /** * @inheritdoc NFTMarketCore * @dev Invalidates the buy price on a auction start, if one is found. */ function _beforeAuctionStarted(address nftContract, uint256 tokenId) internal virtual override { BuyPrice storage buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; if (buyPrice.seller != address(0)) { // A buy price was set for this NFT, invalidate it. _invalidateBuyPrice(nftContract, tokenId); } super._beforeAuctionStarted(nftContract, tokenId); } /** * @notice Process the purchase of an NFT at the current buy price. * @dev The caller must confirm that the seller != address(0) before calling this function. */ function _buy( address nftContract, uint256 tokenId, address payable referrer ) private nonReentrant { BuyPrice memory buyPrice = nftContractToTokenIdToBuyPrice[nftContract][tokenId]; // Remove the buy now price delete nftContractToTokenIdToBuyPrice[nftContract][tokenId]; // Cancel the buyer's offer if there is one in order to free up their FETH balance // even if they don't need the FETH for this specific purchase. _cancelSendersOffer(nftContract, tokenId); if (buyPrice.price > msg.value) { // Withdraw additional ETH required from their available FETH balance. unchecked { // The if above ensures delta will not underflow. uint256 delta = buyPrice.price - msg.value; // Withdraw ETH from the buyer's account in the FETH token contract, // making the ETH available for `_distributeFunds` below. feth.marketWithdrawFrom(msg.sender, delta); } } else if (buyPrice.price < msg.value) { // Return any surplus funds to the buyer. unchecked { // The if above ensures this will not underflow payable(msg.sender).sendValue(msg.value - buyPrice.price); } } // Transfer the NFT to the buyer. // The seller was already authorized when the buyPrice was set originally set. _transferFromEscrow(nftContract, tokenId, msg.sender, address(0)); // Distribute revenue for this sale. (uint256 protocolFee, uint256 creatorFee, uint256 sellerRev) = _distributeFunds( nftContract, tokenId, buyPrice.seller, buyPrice.price, referrer ); emit BuyPriceAccepted(nftContract, tokenId, buyPrice.seller, msg.sender, protocolFee, creatorFee, sellerRev); } /** * @notice Clear a buy price and emit BuyPriceInvalidated. * @dev The caller must confirm the buy price is set before calling this function. */ function _invalidateBuyPrice(address nftContract, uint256 tokenId) private { delete nftContractToTokenIdToBuyPrice[nftContract][tokenId]; emit BuyPriceInvalidated(nftContract, tokenId); } /** * @inheritdoc NFTMarketCore * @dev Invalidates the buy price if one is found before transferring the NFT. * This will revert if there is a buy price set but the `authorizeSeller` is not the owner. */ function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address authorizeSeller ) internal virtual override { address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller != address(0)) { // A buy price was set for this NFT. // `authorizeSeller != address(0) &&` could be added when other mixins use this flow. // ATM that additional check would never return false. if (seller != authorizeSeller) { // When there is a buy price set, the `buyPrice.seller` is the owner of the NFT. revert NFTMarketBuyPrice_Seller_Mismatch(seller); } // The seller authorization has been confirmed. authorizeSeller = address(0); // Invalidate the buy price as the NFT will no longer be in escrow. _invalidateBuyPrice(nftContract, tokenId); } super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller); } /** * @inheritdoc NFTMarketCore * @dev Checks if there is a buy price set, if not then allow the transfer to proceed. */ function _transferFromEscrowIfAvailable( address nftContract, uint256 tokenId, address recipient ) internal virtual override { address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { // A buy price has been set for this NFT so it should remain in escrow. super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient); } } /** * @inheritdoc NFTMarketCore * @dev Checks if the NFT is already in escrow for buy now. */ function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual override { address seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { // The NFT is not in escrow for buy now. super._transferToEscrow(nftContract, tokenId); } else if (seller != msg.sender) { // When there is a buy price set, the `seller` is the owner of the NFT. revert NFTMarketBuyPrice_Seller_Mismatch(seller); } } /** * @notice Returns the buy price details for an NFT if one is available. * @dev If no price is found, seller will be address(0) and price will be max uint256. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return seller The address of the owner that listed a buy price for this NFT. * Returns `address(0)` if there is no buy price set for this NFT. * @return price The price of the NFT. * Returns `0` if there is no buy price set for this NFT. */ function getBuyPrice(address nftContract, uint256 tokenId) external view returns (address seller, uint256 price) { seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { return (seller, type(uint256).max); } price = nftContractToTokenIdToBuyPrice[nftContract][tokenId].price; } /** * @inheritdoc NFTMarketCore * @dev Returns the seller if there is a buy price set for this NFT, otherwise * bubbles the call up for other considerations. */ function _getSellerFor(address nftContract, uint256 tokenId) internal view virtual override returns (address payable seller) { seller = nftContractToTokenIdToBuyPrice[nftContract][tokenId].seller; if (seller == address(0)) { seller = super._getSellerFor(nftContract, tokenId); } } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[1000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "./Constants.sol"; import "../interfaces/IFethMarket.sol"; error NFTMarketCore_FETH_Address_Is_Not_A_Contract(); error NFTMarketCore_Only_FETH_Can_Transfer_ETH(); error NFTMarketCore_Seller_Not_Found(); /** * @title A place for common modifiers and functions used by various NFTMarket mixins, if any. * @dev This also leaves a gap which can be used to add a new mixin to the top of the inheritance tree. */ abstract contract NFTMarketCore is Constants { using AddressUpgradeable for address; /// @notice The FETH ERC-20 token for managing escrow and lockup. IFethMarket internal immutable feth; constructor(address _feth) { if (!_feth.isContract()) { revert NFTMarketCore_FETH_Address_Is_Not_A_Contract(); } feth = IFethMarket(_feth); } /** * @notice Only used by FETH. Any direct transfer from users will revert. */ receive() external payable { if (msg.sender != address(feth)) { revert NFTMarketCore_Only_FETH_Can_Transfer_ETH(); } } /** * @notice If there is a buy price at this amount or lower, accept that and return true. */ function _autoAcceptBuyPrice( address nftContract, uint256 tokenId, uint256 amount ) internal virtual returns (bool); /** * @notice If there is a valid offer at the given price or higher, accept that and return true. */ function _autoAcceptOffer( address nftContract, uint256 tokenId, uint256 minAmount ) internal virtual returns (bool); /** * @notice Notify implementors when an auction has received its first bid. * Once a bid is received the sale is guaranteed to the auction winner * and other sale mechanisms become unavailable. * @dev Implementors of this interface should update internal state to reflect an auction has been kicked off. */ function _beforeAuctionStarted( address, /*nftContract*/ uint256 /*tokenId*/ // solhint-disable-next-line no-empty-blocks ) internal virtual { // No-op } /** * @notice Cancel the `msg.sender`'s offer if there is one, freeing up their FETH balance. * @dev This should be used when it does not make sense to keep the original offer around, * e.g. if a collector accepts a Buy Price then keeping the offer around is not necessary. */ function _cancelSendersOffer(address nftContract, uint256 tokenId) internal virtual; /** * @notice Transfers the NFT from escrow and clears any state tracking this escrowed NFT. * @param authorizeSeller The address of the seller pending authorization. * Once it's been authorized by one of the escrow managers, it should be set to address(0) * indicated that it's no longer pending authorization. */ function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address authorizeSeller ) internal virtual { if (authorizeSeller != address(0)) { revert NFTMarketCore_Seller_Not_Found(); } IERC721(nftContract).transferFrom(address(this), recipient, tokenId); } /** * @notice Transfers the NFT from escrow unless there is another reason for it to remain in escrow. */ function _transferFromEscrowIfAvailable( address nftContract, uint256 tokenId, address recipient ) internal virtual { IERC721(nftContract).transferFrom(address(this), recipient, tokenId); } /** * @notice Transfers an NFT into escrow, * if already there this requires the msg.sender is authorized to manage the sale of this NFT. */ function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual { IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId); } /** * @notice Gets the FETH contract used to escrow offer funds. * @return fethAddress The FETH contract address. */ function getFethAddress() external view returns (address fethAddress) { fethAddress = address(feth); } /** * @dev Determines the minimum amount when increasing an existing offer or bid. */ function _getMinIncrement(uint256 currentAmount) internal pure returns (uint256) { uint256 minIncrement = currentAmount; unchecked { minIncrement /= MIN_PERCENT_INCREMENT_DENOMINATOR; } if (minIncrement == 0) { // Since minIncrement reduces from the currentAmount, this cannot overflow. // The next amount must be at least 1 wei greater than the current. return currentAmount + 1; } return minIncrement + currentAmount; } /** * @notice Checks who the seller for an NFT is, checking escrow or return the current owner if not in escrow. * @dev If the NFT did not have an escrowed seller to return, fall back to return the current owner. */ function _getSellerFor(address nftContract, uint256 tokenId) internal view virtual returns (address payable seller) { seller = payable(IERC721(nftContract).ownerOf(tokenId)); } /** * @notice Checks if an escrowed NFT is currently in active auction. * @return Returns false if the auction has ended, even if it has not yet been settled. */ function _isInActiveAuction(address nftContract, uint256 tokenId) internal view virtual returns (bool); /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps * @dev 50 slots were consumed by adding `ReentrancyGuard`. */ uint256[950] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "./OZ/ERC165Checker.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./Constants.sol"; import "../interfaces/IGetFees.sol"; import "../interfaces/IGetRoyalties.sol"; import "../interfaces/IOwnable.sol"; import "../interfaces/IRoyaltyInfo.sol"; import "../interfaces/ITokenCreator.sol"; import "@manifoldxyz/royalty-registry-solidity/contracts/IRoyaltyRegistry.sol"; error NFTMarketCreators_Address_Does_Not_Support_IRoyaltyRegistry(); /** * @title A mixin for associating creators to NFTs. * @dev In the future this may store creators directly in order to support NFTs created on a different platform. */ abstract contract NFTMarketCreators is Constants, ReentrancyGuardUpgradeable // Adding this unused mixin to help with linearization { using OZERC165Checker for address; IRoyaltyRegistry private immutable royaltyRegistry; /** * @notice Configures the registry allowing for royalty overrides to be defined. * @param _royaltyRegistry The registry to use for royalty overrides. */ constructor(address _royaltyRegistry) { if (!_royaltyRegistry.supportsInterface(type(IRoyaltyRegistry).interfaceId)) { revert NFTMarketCreators_Address_Does_Not_Support_IRoyaltyRegistry(); } royaltyRegistry = IRoyaltyRegistry(_royaltyRegistry); } /** * @notice Looks up the royalty payment configuration for a given NFT. * If more than 5 royalty recipients are defined, only the first 5 are sent royalties - the others are ignored. * The percents distributed will always be normalized to exactly 10%, when multiple recipients are defined * we use the values returned to determine how much of that 10% each recipient should get. * @dev This will check various royalty APIs on the NFT and the royalty override * if one was registered with the royalty registry. This aims to send royalties * in the manner requested by the NFT owner, regardless of where the NFT was minted. */ // solhint-disable-next-line code-complexity function _getCreatorPaymentInfo(address nftContract, uint256 tokenId) internal view returns (address payable[] memory recipients, uint256[] memory splitPerRecipientInBasisPoints) { // All NFTs implement 165 so we skip that check, individual interfaces should return false if 165 is not implemented // 1st priority: ERC-2981 if (nftContract.supportsERC165Interface(type(IRoyaltyInfo).interfaceId)) { try IRoyaltyInfo(nftContract).royaltyInfo{ gas: READ_ONLY_GAS_LIMIT }(tokenId, BASIS_POINTS) returns ( address receiver, uint256 royaltyAmount ) { // Manifold contracts return (address(this), 0) when royalties are not defined // - so ignore results when the amount is 0 if (royaltyAmount > 0) { recipients = new address payable[](1); recipients[0] = payable(receiver); // splitPerRecipientInBasisPoints is not relevant when only 1 recipient is defined return (recipients, splitPerRecipientInBasisPoints); } } catch // solhint-disable-next-line no-empty-blocks { // Fall through } } // 2nd priority: getRoyalties if (nftContract.supportsERC165Interface(type(IGetRoyalties).interfaceId)) { try IGetRoyalties(nftContract).getRoyalties{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( address payable[] memory _recipients, uint256[] memory recipientBasisPoints ) { uint256 recipientLen = _recipients.length; if (recipientLen != 0 && recipientLen == recipientBasisPoints.length) { return (_recipients, recipientBasisPoints); } } catch // solhint-disable-next-line no-empty-blocks { // Fall through } } /* Overrides must support ERC-165 when registered, except for overrides defined by the registry owner. If that results in an override w/o 165 we may need to upgrade the market to support or ignore that override. */ // The registry requires overrides are not 0 and contracts when set. // If no override is set, the nftContract address is returned. try royaltyRegistry.getRoyaltyLookupAddress{ gas: READ_ONLY_GAS_LIMIT }(nftContract) returns ( address overrideContract ) { if (overrideContract != nftContract) { nftContract = overrideContract; // The functions above are repeated here if an override is set. // 3rd priority: ERC-2981 override if (nftContract.supportsERC165Interface(type(IRoyaltyInfo).interfaceId)) { try IRoyaltyInfo(nftContract).royaltyInfo{ gas: READ_ONLY_GAS_LIMIT }(tokenId, BASIS_POINTS) returns ( address receiver, uint256 /* royaltyAmount */ ) { recipients = new address payable[](1); recipients[0] = payable(receiver); // splitPerRecipientInBasisPoints is not relevant when only 1 recipient is defined return (recipients, splitPerRecipientInBasisPoints); } catch // solhint-disable-next-line no-empty-blocks { // Fall through } } // 4th priority: getRoyalties override if (recipients.length == 0 && nftContract.supportsERC165Interface(type(IGetRoyalties).interfaceId)) { try IGetRoyalties(nftContract).getRoyalties{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( address payable[] memory _recipients, uint256[] memory recipientBasisPoints ) { uint256 recipientLen = _recipients.length; if (recipientLen != 0 && recipientLen == recipientBasisPoints.length) { return (_recipients, recipientBasisPoints); } } catch // solhint-disable-next-line no-empty-blocks { // Fall through } } } } catch // solhint-disable-next-line no-empty-blocks { // Ignore out of gas errors and continue using the nftContract address } // 5th priority: getFee* from contract or override if (nftContract.supportsERC165Interface(type(IGetFees).interfaceId)) { try IGetFees(nftContract).getFeeRecipients{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( address payable[] memory _recipients ) { uint256 recipientLen = _recipients.length; if (recipientLen != 0) { try IGetFees(nftContract).getFeeBps{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( uint256[] memory recipientBasisPoints ) { if (recipientLen == recipientBasisPoints.length) { return (_recipients, recipientBasisPoints); } } catch // solhint-disable-next-line no-empty-blocks { // Fall through } } } catch // solhint-disable-next-line no-empty-blocks { // Fall through } } // 6th priority: tokenCreator w/ or w/o requiring 165 from contract or override try ITokenCreator(nftContract).tokenCreator{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( address payable _creator ) { // Only pay the tokenCreator if there wasn't another royalty defined recipients = new address payable[](1); recipients[0] = _creator; // splitPerRecipientInBasisPoints is not relevant when only 1 recipient is defined return (recipients, splitPerRecipientInBasisPoints); } catch // solhint-disable-next-line no-empty-blocks { // Fall through } // 7th priority: owner from contract or override try IOwnable(nftContract).owner{ gas: READ_ONLY_GAS_LIMIT }() returns (address owner) { // Only pay the owner if there wasn't another royalty defined recipients = new address payable[](1); recipients[0] = payable(owner); // splitPerRecipientInBasisPoints is not relevant when only 1 recipient is defined return (recipients, splitPerRecipientInBasisPoints); } catch // solhint-disable-next-line no-empty-blocks { // Fall through } // If no valid payment address or creator is found, return 0 recipients } /** * @notice Returns the address of the registry allowing for royalty configuration overrides. * @return registry The address of the royalty registry contract. */ function getRoyaltyRegistry() public view returns (address registry) { return address(royaltyRegistry); } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps * @dev 500 slots were consumed with the addition of `SendValueWithFallbackWithdraw`. */ uint256[500] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "./Constants.sol"; import "./FoundationTreasuryNode.sol"; import "./SendValueWithFallbackWithdraw.sol"; /** * @title A mixin to distribute funds when an NFT is sold. */ abstract contract NFTMarketFees is Constants, Initializable, FoundationTreasuryNode, SendValueWithFallbackWithdraw { /** * @dev Removing old unused variables in an upgrade safe way. Was: * uint256 private _primaryFoundationFeeBasisPoints; * uint256 private _secondaryFoundationFeeBasisPoints; * uint256 private _secondaryCreatorFeeBasisPoints; * mapping(address => mapping(uint256 => bool)) private _nftContractToTokenIdToFirstSaleCompleted; */ uint256[4] private __gap_was_fees; /// @notice The royalties sent to creator recipients on secondary sales. uint256 private constant CREATOR_ROYALTY_DENOMINATOR = BASIS_POINTS / 1000; // 10% /// @notice The fee collected by Foundation for sales facilitated by this market contract. uint256 private constant PROTOCOL_FEE_DENOMINATOR = BASIS_POINTS / 500; // 5% /// @notice The fee collected by the buy referrer for sales facilitated by this market contract. /// This fee is calculated from the total protocol fee. /// @dev 20% of protocol fee == 1% of total sale. uint256 private constant BUY_REFERRER_PROTOCOL_FEE_DENOMINATOR = 5; /** * @notice Emitted when a NFT sold with a referrer. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param buyReferrer The account which received the buy referral incentive. * @param buyReferrerProtocolFee The portion of the protocol fee collected by the buy referrer. * @param buyReferrerSellerFee The portion of the owner revenue collected by the buy referrer (not implemented). */ event BuyReferralPaid( address indexed nftContract, uint256 indexed tokenId, address buyReferrer, uint256 buyReferrerProtocolFee, uint256 buyReferrerSellerFee ); /** * @notice Distributes funds to foundation, creator recipients, and NFT owner after a sale. */ // solhint-disable-next-line code-complexity function _distributeFunds( address nftContract, uint256 tokenId, address payable seller, uint256 price, address payable buyReferrer ) internal returns ( uint256 protocolFee, uint256 creatorFee, uint256 sellerRev ) { address payable[] memory creatorRecipients; uint256[] memory creatorShares; address payable sellerRevTo; uint256 buyReferrerProtocolFee; uint256 foundationProtocolFee; ( foundationProtocolFee, creatorRecipients, creatorShares, creatorFee, sellerRevTo, sellerRev, buyReferrerProtocolFee ) = _getFees(nftContract, tokenId, seller, price, buyReferrer); // Keep the full fee total in protocolFee for backwards compat with events so that sum of params == sale amount. unchecked { protocolFee = foundationProtocolFee + buyReferrerProtocolFee; } if (buyReferrerProtocolFee != 0) { // Use standard `send` to cap the gas and prevent consuming all available // gas to block a tx from completing successfully. // Fallsback to sending the referral fee to FND on failure. if (_trySendValue(buyReferrer, buyReferrerProtocolFee, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT)) { emit BuyReferralPaid(nftContract, tokenId, buyReferrer, buyReferrerProtocolFee, 0); } else { // If we are unable to pay the referrer than the money is returned to the original protocolFee. unchecked { foundationProtocolFee += buyReferrerProtocolFee; buyReferrerProtocolFee = 0; } } } _sendValueWithFallbackWithdraw( getFoundationTreasury(), foundationProtocolFee, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT ); if (creatorFee != 0) { uint256 creatorRecipientsLength = creatorRecipients.length; if (creatorRecipientsLength > 1) { if (creatorRecipientsLength > MAX_ROYALTY_RECIPIENTS) { creatorRecipientsLength = MAX_ROYALTY_RECIPIENTS; } // Determine the total shares defined so it can be leveraged to distribute below uint256 totalShares; unchecked { // The array length cannot overflow 256 bits. for (uint256 i = 0; i < creatorRecipientsLength; ++i) { if (creatorShares[i] > BASIS_POINTS) { // If the numbers are >100% we ignore the fee recipients and pay just the first instead creatorRecipientsLength = 1; break; } // The check above ensures totalShares wont overflow. totalShares += creatorShares[i]; } } if (totalShares == 0) { creatorRecipientsLength = 1; } // Send payouts to each additional recipient if more than 1 was defined uint256 totalRoyaltiesDistributed; for (uint256 i = 1; i < creatorRecipientsLength; ) { uint256 royalty = (creatorFee * creatorShares[i]) / totalShares; totalRoyaltiesDistributed += royalty; _sendValueWithFallbackWithdraw(creatorRecipients[i], royalty, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS); unchecked { ++i; } } // Send the remainder to the 1st creator, rounding in their favor _sendValueWithFallbackWithdraw( creatorRecipients[0], creatorFee - totalRoyaltiesDistributed, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS ); } else { _sendValueWithFallbackWithdraw(creatorRecipients[0], creatorFee, SEND_VALUE_GAS_LIMIT_MULTIPLE_RECIPIENTS); } } _sendValueWithFallbackWithdraw(sellerRevTo, sellerRev, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT); } /** * @notice Returns how funds will be distributed for a sale at the given price point. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param price The sale price to calculate the fees for. * @return protocolFee How much will be sent to the Foundation treasury. * @return creatorRev How much will be sent across all the `creatorRecipients` defined. * @return creatorRecipients The addresses of the recipients to receive a portion of the creator fee. * @return creatorShares The percentage of the creator fee to be distributed to each `creatorRecipient`. * If there is only one `creatorRecipient`, this may be an empty array. * Otherwise `creatorShares.length` == `creatorRecipients.length`. * @return sellerRev How much will be sent to the owner/seller of the NFT. * If the NFT is being sold by the creator, this may be 0 and the full revenue will appear as `creatorRev`. * @return owner The address of the owner of the NFT. * If `sellerRev` is 0, this may be `address(0)`. */ function getFeesAndRecipients( address nftContract, uint256 tokenId, uint256 price ) external view returns ( uint256 protocolFee, uint256 creatorRev, address payable[] memory creatorRecipients, uint256[] memory creatorShares, uint256 sellerRev, address payable owner ) { address payable seller = _getSellerFor(nftContract, tokenId); // foundationProtocolFee == the full protocolFee since no referrers are defined here. (protocolFee, creatorRecipients, creatorShares, creatorRev, owner, sellerRev, ) = _getFees( nftContract, tokenId, seller, price, address(0) ); } /** * @notice Calculates how funds should be distributed for the given sale details. * @dev When the NFT is being sold by the `tokenCreator`, all the seller revenue will * be split with the royalty recipients defined for that NFT. */ function _getFees( address nftContract, uint256 tokenId, address payable seller, uint256 price, address buyReferrer ) private view returns ( uint256 foundationProtocolFee, address payable[] memory creatorRecipients, uint256[] memory creatorShares, uint256 creatorRev, address payable sellerRevTo, uint256 sellerRev, uint256 buyReferrerProtocolFee ) { address creator; // lookup for tokenCreator try ITokenCreator(nftContract).tokenCreator{ gas: READ_ONLY_GAS_LIMIT }(tokenId) returns ( address payable _creator ) { creator = _creator; } catch // solhint-disable-next-line no-empty-blocks { // Fall through } (creatorRecipients, creatorShares) = _getCreatorPaymentInfo(nftContract, tokenId); // Calculate the protocol fee uint256 protocolFee; unchecked { // SafeMath is not required when dividing by a non-zero constant. protocolFee = price / PROTOCOL_FEE_DENOMINATOR; } if (creatorRecipients.length != 0) { if (seller == creator || (creatorRecipients.length == 1 && seller == creatorRecipients[0])) { // When sold by the creator, all revenue is split if applicable. unchecked { // protocolFee is always < price. creatorRev = price - protocolFee; } } else { // Rounding favors the owner first, then creator, and foundation last. unchecked { // SafeMath is not required when dividing by a non-zero constant. creatorRev = price / CREATOR_ROYALTY_DENOMINATOR; } sellerRevTo = seller; sellerRev = price - protocolFee - creatorRev; } } else { // No royalty recipients found. sellerRevTo = seller; unchecked { // protocolFee is always < price. sellerRev = price - protocolFee; } } // Calculate the buy referrer fee if defined and not a party that already has a vested interest in this sale. // This is done after the sellerRev calculations as a simplification (using the full protocol fee above). if (buyReferrer != address(0) && buyReferrer != msg.sender && buyReferrer != seller && buyReferrer != creator) { // SafeMath is not required since the referrer fee is less than the total protocol fee calculated above. unchecked { buyReferrerProtocolFee = protocolFee / BUY_REFERRER_PROTOCOL_FEE_DENOMINATOR; } } // Calculate the foundation fee using the total protocol fee minus any referrals paid. unchecked { foundationProtocolFee = protocolFee - buyReferrerProtocolFee; } } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[1000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./FoundationTreasuryNode.sol"; import "./NFTMarketCore.sol"; import "./NFTMarketFees.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; error NFTMarketOffer_Cannot_Be_Made_While_In_Auction(); /// @param currentOfferAmount The current highest offer available for this NFT. error NFTMarketOffer_Offer_Below_Min_Amount(uint256 currentOfferAmount); /// @param expiry The time at which the offer had expired. error NFTMarketOffer_Offer_Expired(uint256 expiry); /// @param currentOfferFrom The address of the collector which has made the current highest offer. error NFTMarketOffer_Offer_From_Does_Not_Match(address currentOfferFrom); /// @param minOfferAmount The minimum amount that must be offered in order for it to be accepted. error NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount(uint256 minOfferAmount); error NFTMarketOffer_Reason_Required(); error NFTMarketOffer_Provided_Contract_And_TokenId_Count_Must_Match(); /** * @title Allows collectors to make an offer for an NFT, valid for 24-25 hours. * @notice Funds are escrowed in the FETH ERC-20 token contract. */ abstract contract NFTMarketOffer is FoundationTreasuryNode, NFTMarketCore, ReentrancyGuardUpgradeable, NFTMarketFees { using AddressUpgradeable for address; /// @notice Stores offer details for a specific NFT. struct Offer { // Slot 1: When increasing an offer, only this slot is updated. /// @notice The expiration timestamp of when this offer expires. uint32 expiration; /// @notice The amount, in wei, of the highest offer. uint96 amount; /// @notice First slot (of 16B) used for the offerReferrerAddress. // The offerReferrerAddress is the address used to pay the // referrer on an accepted offer. uint128 offerReferrerAddressSlot0; // Slot 2: When the buyer changes, both slots need updating /// @notice The address of the collector who made this offer. address buyer; /// @notice Second slot (of 4B) used for the offerReferrerAddress. uint32 offerReferrerAddressSlot1; // 96 bits (12B) are available in slot 1. } /// @notice Stores the highest offer for each NFT. mapping(address => mapping(uint256 => Offer)) private nftContractToIdToOffer; /** * @notice Emitted when an offer is accepted, * indicating that the NFT has been transferred and revenue from the sale distributed. * @dev The accepted total offer amount is `protocolFee` + `creatorFee` + `sellerRev`. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param buyer The address of the collector that made the offer which was accepted. * @param seller The address of the seller which accepted the offer. * @param protocolFee The amount of ETH that was sent to Foundation for this sale. * @param creatorFee The amount of ETH that was sent to the creator for this sale. * @param sellerRev The amount of ETH that was sent to the owner for this sale. */ event OfferAccepted( address indexed nftContract, uint256 indexed tokenId, address indexed buyer, address seller, uint256 protocolFee, uint256 creatorFee, uint256 sellerRev ); /** * @notice Emitted when an offer is canceled by a Foundation admin. * @dev This should only be used for extreme cases such as DMCA takedown requests. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param reason The reason for the cancellation (a required field). */ event OfferCanceledByAdmin(address indexed nftContract, uint256 indexed tokenId, string reason); /** * @notice Emitted when an offer is invalidated due to other market activity. * When this occurs, the collector which made the offer has their FETH balance unlocked * and the funds are available to place other offers or to be withdrawn. * @dev This occurs when the offer is no longer eligible to be accepted, * e.g. when a bid is placed in an auction for this NFT. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. */ event OfferInvalidated(address indexed nftContract, uint256 indexed tokenId); /** * @notice Emitted when an offer is made. * @dev The `amount` of the offer is locked in the FETH ERC-20 contract, guaranteeing that the funds * remain available until the `expiration` date. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param buyer The address of the collector that made the offer to buy this NFT. * @param amount The amount, in wei, of the offer. * @param expiration The expiration timestamp for the offer. */ event OfferMade( address indexed nftContract, uint256 indexed tokenId, address indexed buyer, uint256 amount, uint256 expiration ); /** * @notice Accept the highest offer for an NFT. * @dev The offer must not be expired and the NFT owned + approved by the seller or * available in the market contract's escrow. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param offerFrom The address of the collector that you wish to sell to. * If the current highest offer is not from this user, the transaction will revert. * This could happen if a last minute offer was made by another collector, * and would require the seller to try accepting again. * @param minAmount The minimum value of the highest offer for it to be accepted. * If the value is less than this amount, the transaction will revert. * This could happen if the original offer expires and is replaced with a smaller offer. */ function acceptOffer( address nftContract, uint256 tokenId, address offerFrom, uint256 minAmount ) external nonReentrant { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; // Validate offer expiry and amount if (offer.expiration < block.timestamp) { revert NFTMarketOffer_Offer_Expired(offer.expiration); } else if (offer.amount < minAmount) { revert NFTMarketOffer_Offer_Below_Min_Amount(offer.amount); } // Validate the buyer if (offer.buyer != offerFrom) { revert NFTMarketOffer_Offer_From_Does_Not_Match(offer.buyer); } _acceptOffer(nftContract, tokenId); } /** * @notice Allows Foundation to cancel offers. * This will unlock the funds in the FETH ERC-20 contract for the highest offer * and prevent the offer from being accepted. * @dev This should only be used for extreme cases such as DMCA takedown requests. * @param nftContracts The addresses of the NFT contracts to cancel. This must be the same length as `tokenIds`. * @param tokenIds The ids of the NFTs to cancel. This must be the same length as `nftContracts`. * @param reason The reason for the cancellation (a required field). */ function adminCancelOffers( address[] calldata nftContracts, uint256[] calldata tokenIds, string calldata reason ) external onlyFoundationAdmin nonReentrant { if (bytes(reason).length == 0) { revert NFTMarketOffer_Reason_Required(); } if (nftContracts.length != tokenIds.length) { revert NFTMarketOffer_Provided_Contract_And_TokenId_Count_Must_Match(); } // The array length cannot overflow 256 bits unchecked { for (uint256 i = 0; i < nftContracts.length; ++i) { Offer memory offer = nftContractToIdToOffer[nftContracts[i]][tokenIds[i]]; delete nftContractToIdToOffer[nftContracts[i]][tokenIds[i]]; if (offer.expiration >= block.timestamp) { // Unlock from escrow and emit an event only if the offer is still active feth.marketUnlockFor(offer.buyer, offer.expiration, offer.amount); emit OfferCanceledByAdmin(nftContracts[i], tokenIds[i], reason); } // Else continue on so the rest of the batch transaction can process successfully } } } /** * @notice Make an offer for any NFT which is valid for 24-25 hours. * The funds will be locked in the FETH token contract and become available once the offer is outbid or has expired. * @dev An offer may be made for an NFT before it is minted, although we generally not recommend you do that. * If there is a buy price set at this price or lower, that will be accepted instead of making an offer. * `msg.value` must be <= `amount` and any delta will be taken from the account's available FETH balance. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param amount The amount to offer for this NFT. * @param referrer The refrerrer address for the offer. * @return expiration The timestamp for when this offer will expire. * This is provided as a return value in case another contract would like to leverage this information, * user's should refer to the expiration in the `OfferMade` event log. * If the buy price is accepted instead, `0` is returned as the expiration since that's n/a. */ function makeOfferV2( address nftContract, uint256 tokenId, uint256 amount, address payable referrer ) public payable returns (uint256 expiration) { // If there is a buy price set at this price or lower, accept that instead. if (_autoAcceptBuyPrice(nftContract, tokenId, amount)) { // If the buy price is accepted, `0` is returned as the expiration since that's n/a. return 0; } if (_isInActiveAuction(nftContract, tokenId)) { revert NFTMarketOffer_Cannot_Be_Made_While_In_Auction(); } Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.expiration < block.timestamp) { // This is a new offer for the NFT (no other offer found or the previous offer expired) // Lock the offer amount in FETH until the offer expires in 24-25 hours. expiration = feth.marketLockupFor{ value: msg.value }(msg.sender, amount); } else { // A previous offer exists and has not expired uint256 minIncrement = _getMinIncrement(offer.amount); if (amount < minIncrement) { // A non-trivial increase in price is required to avoid sniping revert NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount(minIncrement); } // Unlock the previous offer so that the FETH tokens are available for other offers or to transfer / withdraw // and lock the new offer amount in FETH until the offer expires in 24-25 hours. expiration = feth.marketChangeLockup{ value: msg.value }( offer.buyer, offer.expiration, offer.amount, msg.sender, amount ); } // Record offer details offer.buyer = msg.sender; // The FETH contract guarantees that the expiration fits into 32 bits. offer.expiration = uint32(expiration); // `amount` is capped by the ETH provided, which cannot realistically overflow 96 bits. offer.amount = uint96(amount); // Set offerReferrerAddressSlot0 to the first 16B of the referrer address. // By shifting the referrer 32 bits to the right we obtain the first 16B. offer.offerReferrerAddressSlot0 = uint128(uint160(address(referrer)) >> 32); // Set offerReferrerAddressSlot1 to the last 4B of the referrer address. // By casting the referrer address to 32bits we discard the first 16B. offer.offerReferrerAddressSlot1 = uint32(uint160(address(referrer))); emit OfferMade(nftContract, tokenId, msg.sender, amount, expiration); } /** * @notice [DEPRECATED] Please use `makeOfferV2`. */ function makeOffer( address nftContract, uint256 tokenId, uint256 amount ) external payable returns (uint256 expiration) { expiration = makeOfferV2(nftContract, tokenId, amount, payable(0)); } /** * @notice Accept the highest offer for an NFT from the `msg.sender` account. * The NFT will be transferred to the buyer and revenue from the sale will be distributed. * @dev The caller must validate the expiry and amount before calling this helper. * This may invalidate other market tools, such as clearing the buy price if set. */ function _acceptOffer(address nftContract, uint256 tokenId) private { Offer memory offer = nftContractToIdToOffer[nftContract][tokenId]; // Remove offer delete nftContractToIdToOffer[nftContract][tokenId]; // Withdraw ETH from the buyer's account in the FETH token contract. feth.marketWithdrawLocked(offer.buyer, offer.expiration, offer.amount); // Transfer the NFT to the buyer. try IERC721(nftContract).transferFrom(msg.sender, offer.buyer, tokenId) // solhint-disable-next-line no-empty-blocks { // NFT was in the seller's wallet so the transfer is complete. } catch { // If the transfer fails then attempt to transfer from escrow instead. // This should revert if `msg.sender` is not the owner of this NFT. _transferFromEscrow(nftContract, tokenId, offer.buyer, msg.sender); } // Distribute revenue for this sale leveraging the ETH received from the FETH contract in the line above. (uint256 protocolFee, uint256 creatorFee, uint256 sellerRev) = _distributeFunds( nftContract, tokenId, payable(msg.sender), offer.amount, _getOfferReferrerFromSlots(offer.offerReferrerAddressSlot0, offer.offerReferrerAddressSlot1) ); emit OfferAccepted(nftContract, tokenId, offer.buyer, msg.sender, protocolFee, creatorFee, sellerRev); } /** * @inheritdoc NFTMarketCore * @dev Invalidates the highest offer when an auction is kicked off, if one is found. */ function _beforeAuctionStarted(address nftContract, uint256 tokenId) internal virtual override { _invalidateOffer(nftContract, tokenId); super._beforeAuctionStarted(nftContract, tokenId); } /** * @inheritdoc NFTMarketCore */ function _autoAcceptOffer( address nftContract, uint256 tokenId, uint256 minAmount ) internal override returns (bool) { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.expiration < block.timestamp || offer.amount < minAmount) { // No offer found, the most recent offer is now expired, or the highest offer is below the minimum amount. return false; } _acceptOffer(nftContract, tokenId); return true; } /** * @inheritdoc NFTMarketCore */ function _cancelSendersOffer(address nftContract, uint256 tokenId) internal override { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.buyer == msg.sender) { _invalidateOffer(nftContract, tokenId); } } /** * @notice Invalidates the offer and frees ETH from escrow, if the offer has not already expired. * @dev Offers are not invalidated when the NFT is purchased by accepting the buy price unless it * was purchased by the same user. * The user which just purchased the NFT may have buyer's remorse and promptly decide they want a fast exit, * accepting a small loss to limit their exposure. */ function _invalidateOffer(address nftContract, uint256 tokenId) private { if (nftContractToIdToOffer[nftContract][tokenId].expiration >= block.timestamp) { // An offer was found and it has not already expired Offer memory offer = nftContractToIdToOffer[nftContract][tokenId]; // Remove offer delete nftContractToIdToOffer[nftContract][tokenId]; // Unlock the offer so that the FETH tokens are available for other offers or to transfer / withdraw feth.marketUnlockFor(offer.buyer, offer.expiration, offer.amount); emit OfferInvalidated(nftContract, tokenId); } } /** * @notice Returns the minimum amount a collector must offer for this NFT in order for the offer to be valid. * @dev Offers for this NFT which are less than this value will revert. * Once the previous offer has expired smaller offers can be made. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return minimum The minimum amount that must be offered for this NFT. */ function getMinOfferAmount(address nftContract, uint256 tokenId) external view returns (uint256 minimum) { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.expiration >= block.timestamp) { return _getMinIncrement(offer.amount); } // Absolute min is anything > 0 return 1; } /** * @notice Returns details about the current highest offer for an NFT. * @dev Default values are returned if there is no offer or the offer has expired. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return buyer The address of the buyer that made the current highest offer. * Returns `address(0)` if there is no offer or the most recent offer has expired. * @return expiration The timestamp that the current highest offer expires. * Returns `0` if there is no offer or the most recent offer has expired. * @return amount The amount being offered for this NFT. * Returns `0` if there is no offer or the most recent offer has expired. */ function getOffer(address nftContract, uint256 tokenId) external view returns ( address buyer, uint256 expiration, uint256 amount ) { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.expiration < block.timestamp) { // Offer not found or has expired return (address(0), 0, 0); } // An offer was found and it has not yet expired. return (offer.buyer, offer.expiration, offer.amount); } /** * @notice Returns the current highest offer's referral for an NFT. * @dev Default value of `payable(0)` is returned if * there is no offer, the offer has expired or does not have a referral. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return referrer The payable address of the referrer for the offer. */ function getOfferReferrer(address nftContract, uint256 tokenId) external view returns (address payable referrer) { Offer storage offer = nftContractToIdToOffer[nftContract][tokenId]; if (offer.expiration < block.timestamp) { // Offer not found or has expired return payable(0); } return _getOfferReferrerFromSlots(offer.offerReferrerAddressSlot0, offer.offerReferrerAddressSlot1); } function _getOfferReferrerFromSlots(uint128 offerReferrerAddressSlot0, uint32 offerReferrerAddressSlot1) private pure returns (address payable referrer) { // Stitch offerReferrerAddressSlot0 and offerReferrerAddressSlot1 to obtain the payable offerReferrerAddress. // Left shift offerReferrerAddressSlot0 by 32 bits OR it with offerReferrerAddressSlot1. referrer = payable(address((uint160(offerReferrerAddressSlot0) << 32) | uint160(offerReferrerAddressSlot1))); } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[1000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "./NFTMarketFees.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; error NFTMarketPrivateSale_Can_Be_Offered_For_24Hrs_Max(); error NFTMarketPrivateSale_Signature_Canceled_Or_Already_Claimed(); error NFTMarketPrivateSale_Proxy_Address_Is_Not_A_Contract(); error NFTMarketPrivateSale_Sale_Expired(); error NFTMarketPrivateSale_Signature_Verification_Failed(); error NFTMarketPrivateSale_Too_Much_Value_Provided(); /** * @title Allows owners to offer an NFT for sale to a specific collector. * @notice Private sales are authorized by the seller with an EIP-712 signature. * @dev Private sale offers must be accepted by the buyer before they expire, typically in 24 hours. */ abstract contract NFTMarketPrivateSale is NFTMarketFees { using AddressUpgradeable for address; using ECDSA for bytes32; /// @dev This value was replaced with an immutable version. bytes32 private __gap_was_DOMAIN_SEPARATOR; /// @notice Tracks if a private sale has already been used. /// @dev Maps nftContract -> tokenId -> buyer -> seller -> amount -> deadline -> invalidated. // solhint-disable-next-line max-line-length mapping(address => mapping(uint256 => mapping(address => mapping(address => mapping(uint256 => mapping(uint256 => bool)))))) private privateSaleInvalidated; /// @notice The domain used in EIP-712 signatures. /// @dev It is not a constant so that the chainId can be determined dynamically. /// If multiple classes use EIP-712 signatures in the future this can move to a shared file. bytes32 private immutable DOMAIN_SEPARATOR; /// @notice The hash of the private sale method signature used for EIP-712 signatures. bytes32 private constant BUY_FROM_PRIVATE_SALE_TYPEHASH = keccak256("BuyFromPrivateSale(address nftContract,uint256 tokenId,address buyer,uint256 price,uint256 deadline)"); /// @notice The name used in the EIP-712 domain. /// @dev If multiple classes use EIP-712 signatures in the future this can move to the shared constants file. string private constant NAME = "FNDNFTMarket"; /** * @notice Emitted when an NFT is sold in a private sale. * @dev The total amount of this sale is `protocolFee` + `creatorFee` + `sellerRev`. * @param nftContract The address of the NFT contract. * @param tokenId The ID of the NFT. * @param seller The address of the seller. * @param buyer The address of the buyer. * @param protocolFee The amount of ETH that was sent to Foundation for this sale. * @param creatorFee The amount of ETH that was sent to the creator for this sale. * @param sellerRev The amount of ETH that was sent to the owner for this sale. * @param deadline When the private sale offer was set to expire. */ event PrivateSaleFinalized( address indexed nftContract, uint256 indexed tokenId, address indexed seller, address buyer, uint256 protocolFee, uint256 creatorFee, uint256 sellerRev, uint256 deadline ); /** * @notice Configures the contract to accept EIP-712 signatures. * @param marketProxyAddress The address of the proxy contract which will be called when accepting a private sale. */ constructor(address marketProxyAddress) { if (!marketProxyAddress.isContract()) { revert NFTMarketPrivateSale_Proxy_Address_Is_Not_A_Contract(); } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(NAME)), // Incrementing the version can be used to invalidate previously signed messages. keccak256(bytes("1")), block.chainid, marketProxyAddress ) ); } /** * @notice Buy an NFT from a private sale. * @dev This API is deprecated and will be removed in the future, `buyFromPrivateSaleFor` should be used instead. * The seller signs a message approving the sale and then the buyer calls this function * with the `msg.value` equal to the agreed upon price. * If the seller is no longer the `ownerOf` this NFT or has removed approval for this contract, * attempts to purchase from private sale will revert. * @param nftContract The address of the NFT contract. * @param tokenId The ID of the NFT. * @param deadline The timestamp at which the offer to sell will expire. * @param v The v value of the EIP-712 signature. * @param r The r value of the EIP-712 signature. * @param s The s value of the EIP-712 signature. */ function buyFromPrivateSale( address nftContract, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable { buyFromPrivateSaleFor(nftContract, tokenId, msg.value, deadline, v, r, s); } /** * @notice Buy an NFT from a private sale. * @dev The seller signs a message approving the sale and then the buyer calls this function * with the `amount` equal to the agreed upon price. * If the seller is no longer the `ownerOf` this NFT or has removed approval for this contract, * attempts to purchase from private sale will revert. * @dev `amount` - `msg.value` is withdrawn from the bidder's FETH balance. * @param nftContract The address of the NFT contract. * @param tokenId The ID of the NFT. * @param amount The amount to buy for, if this is more than `msg.value` funds will be * withdrawn from your FETH balance. * @param deadline The timestamp at which the offer to sell will expire. * @param v The v value of the EIP-712 signature. * @param r The r value of the EIP-712 signature. * @param s The s value of the EIP-712 signature. */ function buyFromPrivateSaleFor( address nftContract, uint256 tokenId, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public payable nonReentrant { // now + 2 days cannot overflow unchecked { if (deadline < block.timestamp) { // The signed message from the seller has expired. revert NFTMarketPrivateSale_Sale_Expired(); } else if (deadline > block.timestamp + 2 days) { // Private sales typically expire in 24 hours, but 2 days is used here in order to ensure // that transactions do not fail due to a minor timezone error or similar during signing. // This prevents malicious actors from requesting signatures that never expire. revert NFTMarketPrivateSale_Can_Be_Offered_For_24Hrs_Max(); } } // Cancel the buyer's offer if there is one in order to free up their FETH balance // even if they don't need the FETH for this specific purchase. _cancelSendersOffer(address(nftContract), tokenId); if (amount > msg.value) { // Withdraw additional ETH required from their available FETH balance. unchecked { // The if above ensures delta will not underflow feth.marketWithdrawFrom(msg.sender, amount - msg.value); } } else if (amount < msg.value) { // The terms of the sale cannot change, so if too much ETH is sent then something went wrong. revert NFTMarketPrivateSale_Too_Much_Value_Provided(); } // The seller must have the NFT in their wallet when this function is called, // otherwise the signature verification below will fail. address payable seller = payable(IERC721(nftContract).ownerOf(tokenId)); // Ensure that the offer can only be accepted once. if (privateSaleInvalidated[nftContract][tokenId][msg.sender][seller][amount][deadline]) { revert NFTMarketPrivateSale_Signature_Canceled_Or_Already_Claimed(); } privateSaleInvalidated[nftContract][tokenId][msg.sender][seller][amount][deadline] = true; // Scoping this block to avoid a stack too deep error { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(BUY_FROM_PRIVATE_SALE_TYPEHASH, nftContract, tokenId, msg.sender, amount, deadline)) ) ); // Revert if the signature is invalid, the terms are not as expected, or if the seller transferred the NFT. if (digest.recover(v, r, s) != seller) { revert NFTMarketPrivateSale_Signature_Verification_Failed(); } } // This should revert if the seller has not given the market contract approval. IERC721(nftContract).transferFrom(seller, msg.sender, tokenId); // Distribute revenue for this sale. (uint256 protocolFee, uint256 creatorFee, uint256 sellerRev) = _distributeFunds( nftContract, tokenId, seller, amount, payable(address(0)) ); emit PrivateSaleFinalized(nftContract, tokenId, seller, msg.sender, protocolFee, creatorFee, sellerRev, deadline); } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps * @dev 1 slot was consumed by privateSaleInvalidated. */ uint256[999] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./Constants.sol"; import "./FoundationTreasuryNode.sol"; import "./NFTMarketAuction.sol"; import "./NFTMarketCore.sol"; import "./NFTMarketFees.sol"; import "./SendValueWithFallbackWithdraw.sol"; /// @param auctionId The already listed auctionId for this NFT. error NFTMarketReserveAuction_Already_Listed(uint256 auctionId); /// @param minAmount The minimum amount that must be bid in order for it to be accepted. error NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount(uint256 minAmount); error NFTMarketReserveAuction_Cannot_Admin_Cancel_Without_Reason(); /// @param reservePrice The current reserve price. error NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price(uint256 reservePrice); /// @param endTime The timestamp at which the auction had ended. error NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction(uint256 endTime); error NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction(); error NFTMarketReserveAuction_Cannot_Cancel_Nonexistent_Auction(); error NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction(); /// @param endTime The timestamp at which the auction will end. error NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress(uint256 endTime); error NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid(); error NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress(); /// @param maxDuration The maximum configuration for a duration of the auction, in seconds. error NFTMarketReserveAuction_Exceeds_Max_Duration(uint256 maxDuration); /// @param extensionDuration The extension duration, in seconds. error NFTMarketReserveAuction_Less_Than_Extension_Duration(uint256 extensionDuration); error NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price(); /// @param seller The current owner of the NFT. error NFTMarketReserveAuction_Not_Matching_Seller(address seller); /// @param owner The current owner of the NFT. error NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(address owner); error NFTMarketReserveAuction_Price_Already_Set(); error NFTMarketReserveAuction_Too_Much_Value_Provided(); /** * @title Allows the owner of an NFT to list it in auction. * @notice NFTs in auction are escrowed in the market contract. * @dev There is room to optimize the storage for auctions, significantly reducing gas costs. * This may be done in the future, but for now it will remain as is in order to ease upgrade compatibility. */ abstract contract NFTMarketReserveAuction is Constants, FoundationTreasuryNode, NFTMarketCore, ReentrancyGuardUpgradeable, SendValueWithFallbackWithdraw, NFTMarketFees, NFTMarketAuction { /// @notice The auction configuration for a specific NFT. struct ReserveAuction { /// @notice The address of the NFT contract. address nftContract; /// @notice The id of the NFT. uint256 tokenId; /// @notice The owner of the NFT which listed it in auction. address payable seller; /// @notice The duration for this auction. uint256 duration; /// @notice The extension window for this auction. uint256 extensionDuration; /// @notice The time at which this auction will not accept any new bids. /// @dev This is `0` until the first bid is placed. uint256 endTime; /// @notice The current highest bidder in this auction. /// @dev This is `address(0)` until the first bid is placed. address payable bidder; /// @notice The latest price of the NFT in this auction. /// @dev This is set to the reserve price, and then to the highest bid once the auction has started. uint256 amount; } /// @notice Stores the auction configuration for a specific NFT. /// @dev This allows us to modify the storage struct without changing external APIs. struct ReserveAuctionStorage { /// @notice The address of the NFT contract. address nftContract; /// @notice The id of the NFT. uint256 tokenId; /// @notice The owner of the NFT which listed it in auction. address payable seller; /// @notice First slot (of 12B) used for the bidReferrerAddress. /// The bidReferrerAddress is the address used to pay the referrer on finalize. /// @dev This approach is used in order to pack storage, saving gas. uint96 bidReferrerAddressSlot0; /// @dev This field is no longer used. uint256 __gap_was_duration; /// @dev This field is no longer used. uint256 __gap_was_extensionDuration; /// @notice The time at which this auction will not accept any new bids. /// @dev This is `0` until the first bid is placed. uint256 endTime; /// @notice The current highest bidder in this auction. /// @dev This is `address(0)` until the first bid is placed. address payable bidder; /// @notice Second slot (of 8B) used for the bidReferrerAddress. uint64 bidReferrerAddressSlot1; /// @notice The latest price of the NFT in this auction. /// @dev This is set to the reserve price, and then to the highest bid once the auction has started. uint256 amount; } /// @notice The auction configuration for a specific auction id. mapping(address => mapping(uint256 => uint256)) private nftContractToTokenIdToAuctionId; /// @notice The auction id for a specific NFT. /// @dev This is deleted when an auction is finalized or canceled. mapping(uint256 => ReserveAuctionStorage) private auctionIdToAuction; /** * @dev Removing old unused variables in an upgrade safe way. Was: * uint256 private __gap_was_minPercentIncrementInBasisPoints; * uint256 private __gap_was_maxBidIncrementRequirement; * uint256 private __gap_was_duration; * uint256 private __gap_was_extensionDuration; * uint256 private __gap_was_goLiveDate; */ uint256[5] private __gap_was_config; /// @notice How long an auction lasts for once the first bid has been received. uint256 private immutable DURATION; /// @notice The window for auction extensions, any bid placed in the final 15 minutes /// of an auction will reset the time remaining to 15 minutes. uint256 private constant EXTENSION_DURATION = 15 minutes; /// @notice Caps the max duration that may be configured so that overflows will not occur. uint256 private constant MAX_MAX_DURATION = 1000 days; /** * @notice Emitted when a bid is placed. * @param auctionId The id of the auction this bid was for. * @param bidder The address of the bidder. * @param amount The amount of the bid. * @param endTime The new end time of the auction (which may have been set or extended by this bid). */ event ReserveAuctionBidPlaced(uint256 indexed auctionId, address indexed bidder, uint256 amount, uint256 endTime); /** * @notice Emitted when an auction is cancelled. * @dev This is only possible if the auction has not received any bids. * @param auctionId The id of the auction that was cancelled. */ event ReserveAuctionCanceled(uint256 indexed auctionId); /** * @notice Emitted when an auction is canceled by a Foundation admin. * @dev When this occurs, the highest bidder (if there was a bid) is automatically refunded. * @param auctionId The id of the auction that was cancelled. * @param reason The reason for the cancellation. */ event ReserveAuctionCanceledByAdmin(uint256 indexed auctionId, string reason); /** * @notice Emitted when an NFT is listed for auction. * @param seller The address of the seller. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param duration The duration of the auction (always 24-hours). * @param extensionDuration The duration of the auction extension window (always 15-minutes). * @param reservePrice The reserve price to kick off the auction. * @param auctionId The id of the auction that was created. */ event ReserveAuctionCreated( address indexed seller, address indexed nftContract, uint256 indexed tokenId, uint256 duration, uint256 extensionDuration, uint256 reservePrice, uint256 auctionId ); /** * @notice Emitted when an auction that has already ended is finalized, * indicating that the NFT has been transferred and revenue from the sale distributed. * @dev The amount of the highest bid / final sale price for this auction * is `protocolFee` + `creatorFee` + `sellerRev`. * @param auctionId The id of the auction that was finalized. * @param seller The address of the seller. * @param bidder The address of the highest bidder that won the NFT. * @param protocolFee The amount of ETH that was sent to Foundation for this sale. * @param creatorFee The amount of ETH that was sent to the creator for this sale. * @param sellerRev The amount of ETH that was sent to the owner for this sale. */ event ReserveAuctionFinalized( uint256 indexed auctionId, address indexed seller, address indexed bidder, uint256 protocolFee, uint256 creatorFee, uint256 sellerRev ); /** * @notice Emitted when an auction is invalidated due to other market activity. * @dev This occurs when the NFT is sold another way, such as with `buy` or `acceptOffer`. * @param auctionId The id of the auction that was invalidated. */ event ReserveAuctionInvalidated(uint256 indexed auctionId); /** * @notice Emitted when the auction's reserve price is changed. * @dev This is only possible if the auction has not received any bids. * @param auctionId The id of the auction that was updated. * @param reservePrice The new reserve price for the auction. */ event ReserveAuctionUpdated(uint256 indexed auctionId, uint256 reservePrice); /// @notice Confirms that the reserve price is not zero. modifier onlyValidAuctionConfig(uint256 reservePrice) { if (reservePrice == 0) { revert NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price(); } _; } /** * @notice Configures the duration for auctions. * @param duration The duration for auctions, in seconds. */ constructor(uint256 duration) { if (duration > MAX_MAX_DURATION) { // This ensures that math in this file will not overflow due to a huge duration. revert NFTMarketReserveAuction_Exceeds_Max_Duration(MAX_MAX_DURATION); } if (duration < EXTENSION_DURATION) { // The auction duration configuration must be greater than the extension window of 15 minutes revert NFTMarketReserveAuction_Less_Than_Extension_Duration(EXTENSION_DURATION); } DURATION = duration; } /** * @notice Allows Foundation to cancel an auction, refunding the bidder and returning the NFT to * the seller (if not active buy price set). * This should only be used for extreme cases such as DMCA takedown requests. * @param auctionId The id of the auction to cancel. * @param reason The reason for the cancellation (a required field). */ function adminCancelReserveAuction(uint256 auctionId, string calldata reason) external onlyFoundationAdmin nonReentrant { if (bytes(reason).length == 0) { revert NFTMarketReserveAuction_Cannot_Admin_Cancel_Without_Reason(); } ReserveAuctionStorage memory auction = auctionIdToAuction[auctionId]; if (auction.amount == 0) { revert NFTMarketReserveAuction_Cannot_Cancel_Nonexistent_Auction(); } delete nftContractToTokenIdToAuctionId[auction.nftContract][auction.tokenId]; delete auctionIdToAuction[auctionId]; // Return the NFT to the owner. _transferFromEscrowIfAvailable(auction.nftContract, auction.tokenId, auction.seller); if (auction.bidder != address(0)) { // Refund the highest bidder if any bids were placed in this auction. _sendValueWithFallbackWithdraw(auction.bidder, auction.amount, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT); } emit ReserveAuctionCanceledByAdmin(auctionId, reason); } /** * @notice If an auction has been created but has not yet received bids, it may be canceled by the seller. * @dev The NFT is transferred back to the owner unless there is still a buy price set. * @param auctionId The id of the auction to cancel. */ function cancelReserveAuction(uint256 auctionId) external nonReentrant { ReserveAuctionStorage memory auction = auctionIdToAuction[auctionId]; if (auction.seller != msg.sender) { revert NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(auction.seller); } if (auction.endTime != 0) { revert NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress(); } // Remove the auction. delete nftContractToTokenIdToAuctionId[auction.nftContract][auction.tokenId]; delete auctionIdToAuction[auctionId]; // Transfer the NFT unless it still has a buy price set. _transferFromEscrowIfAvailable(auction.nftContract, auction.tokenId, auction.seller); emit ReserveAuctionCanceled(auctionId); } /** * @notice Creates an auction for the given NFT. * The NFT is held in escrow until the auction is finalized or canceled. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @param reservePrice The initial reserve price for the auction. */ function createReserveAuction( address nftContract, uint256 tokenId, uint256 reservePrice ) external nonReentrant onlyValidAuctionConfig(reservePrice) { uint256 auctionId = _getNextAndIncrementAuctionId(); // If the `msg.sender` is not the owner of the NFT, transferring into escrow should fail. _transferToEscrow(nftContract, tokenId); // This check must be after _transferToEscrow in case auto-settle was required if (nftContractToTokenIdToAuctionId[nftContract][tokenId] != 0) { revert NFTMarketReserveAuction_Already_Listed(nftContractToTokenIdToAuctionId[nftContract][tokenId]); } // Store the auction details nftContractToTokenIdToAuctionId[nftContract][tokenId] = auctionId; ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; auction.nftContract = nftContract; auction.tokenId = tokenId; auction.seller = payable(msg.sender); auction.amount = reservePrice; emit ReserveAuctionCreated(msg.sender, nftContract, tokenId, DURATION, EXTENSION_DURATION, reservePrice, auctionId); } /** * @notice Once the countdown has expired for an auction, anyone can settle the auction. * This will send the NFT to the highest bidder and distribute revenue for this sale. * @param auctionId The id of the auction to settle. */ function finalizeReserveAuction(uint256 auctionId) external nonReentrant { if (auctionIdToAuction[auctionId].endTime == 0) { revert NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction(); } _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: false }); } /** * @notice Place a bid in an auction. * A bidder may place a bid which is at least the value defined by `getMinBidAmount`. * If this is the first bid on the auction, the countdown will begin. * If there is already an outstanding bid, the previous bidder will be refunded at this time * and if the bid is placed in the final moments of the auction, the countdown may be extended. * @dev This API is deprecated and will be removed in the future, `placeBidV2` should be used instead. * @param auctionId The id of the auction to bid on. */ function placeBid(uint256 auctionId) external payable { placeBidV2(auctionId, msg.value, payable(0)); } /** * @notice Place a bid in an auction. * A bidder may place a bid which is at least the amount defined by `getMinBidAmount`. * If this is the first bid on the auction, the countdown will begin. * If there is already an outstanding bid, the previous bidder will be refunded at this time * and if the bid is placed in the final moments of the auction, the countdown may be extended. * @dev `amount` - `msg.value` is withdrawn from the bidder's FETH balance. * @param auctionId The id of the auction to bid on. * @param amount The amount to bid, if this is more than `msg.value` funds will be withdrawn from your FETH balance. */ /* solhint-disable-next-line code-complexity */ function placeBidV2( uint256 auctionId, uint256 amount, address payable referrer ) public payable nonReentrant { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.amount == 0) { // No auction found revert NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction(); } else if (amount < msg.value) { // The amount is specified by the bidder, so if too much ETH is sent then something went wrong. revert NFTMarketReserveAuction_Too_Much_Value_Provided(); } uint256 endTime = auction.endTime; // Store the bid referral if (referrer != address(0) || endTime != 0) { auction.bidReferrerAddressSlot0 = uint96(uint160(address(referrer)) >> 64); auction.bidReferrerAddressSlot1 = uint64(uint160(address(referrer))); } if (endTime == 0) { // This is the first bid, kicking off the auction. if (amount < auction.amount) { // The bid must be >= the reserve price. revert NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price(auction.amount); } // Notify other market tools that an auction for this NFT has been kicked off. // The only state change before this call is potentially withdrawing funds from FETH. _beforeAuctionStarted(auction.nftContract, auction.tokenId); // Store the bid details. auction.amount = amount; auction.bidder = payable(msg.sender); // On the first bid, set the endTime to now + duration. unchecked { // Duration is always set to 24hrs so the below can't overflow. endTime = block.timestamp + DURATION; } auction.endTime = endTime; } else { if (endTime < block.timestamp) { // The auction has already ended. revert NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction(endTime); } else if (auction.bidder == msg.sender) { // We currently do not allow a bidder to increase their bid unless another user has outbid them first. revert NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid(); } else { uint256 minIncrement = _getMinIncrement(auction.amount); if (amount < minIncrement) { // If this bid outbids another, it must be at least 10% greater than the last bid. revert NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount(minIncrement); } } // Cache and update bidder state uint256 originalAmount = auction.amount; address payable originalBidder = auction.bidder; auction.amount = amount; auction.bidder = payable(msg.sender); unchecked { // When a bid outbids another, check to see if a time extension should apply. // We confirmed that the auction has not ended, so endTime is always >= the current timestamp. // Current time plus extension duration (always 15 mins) cannot overflow. uint256 endTimeWithExtension = block.timestamp + EXTENSION_DURATION; if (endTime < endTimeWithExtension) { endTime = endTimeWithExtension; auction.endTime = endTime; } } // Refund the previous bidder _sendValueWithFallbackWithdraw(originalBidder, originalAmount, SEND_VALUE_GAS_LIMIT_SINGLE_RECIPIENT); } // Withdraw last in order to leverage freed FETH balance. if (amount > msg.value) { // Withdraw additional ETH required from their available FETH balance. unchecked { // The if above ensures delta will not underflow. // Withdraw ETH from the buyer's account in the FETH token contract. feth.marketWithdrawFrom(msg.sender, amount - msg.value); } } emit ReserveAuctionBidPlaced(auctionId, msg.sender, amount, endTime); } /** * @notice If an auction has been created but has not yet received bids, the reservePrice may be * changed by the seller. * @param auctionId The id of the auction to change. * @param reservePrice The new reserve price for this auction. */ function updateReserveAuction(uint256 auctionId, uint256 reservePrice) external onlyValidAuctionConfig(reservePrice) { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.seller != msg.sender) { revert NFTMarketReserveAuction_Only_Owner_Can_Update_Auction(auction.seller); } else if (auction.endTime != 0) { revert NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress(); } else if (auction.amount == reservePrice) { revert NFTMarketReserveAuction_Price_Already_Set(); } // Update the current reserve price. auction.amount = reservePrice; emit ReserveAuctionUpdated(auctionId, reservePrice); } /** * @notice Settle an auction that has already ended. * This will send the NFT to the highest bidder and distribute revenue for this sale. * @param keepInEscrow If true, the NFT will be kept in escrow to save gas by avoiding * redundant transfers if the NFT should remain in escrow, such as when the new owner * sets a buy price or lists it in a new auction. */ function _finalizeReserveAuction(uint256 auctionId, bool keepInEscrow) private { ReserveAuctionStorage memory auction = auctionIdToAuction[auctionId]; if (auction.endTime >= block.timestamp) { revert NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress(auction.endTime); } // Remove the auction. delete nftContractToTokenIdToAuctionId[auction.nftContract][auction.tokenId]; delete auctionIdToAuction[auctionId]; if (!keepInEscrow) { // The seller was authorized when the auction was originally created super._transferFromEscrow(auction.nftContract, auction.tokenId, auction.bidder, address(0)); } // Distribute revenue for this sale. (uint256 protocolFee, uint256 creatorFee, uint256 sellerRev) = _distributeFunds( auction.nftContract, auction.tokenId, auction.seller, auction.amount, payable(address((uint160(auction.bidReferrerAddressSlot0) << 64) | uint160(auction.bidReferrerAddressSlot1))) ); emit ReserveAuctionFinalized(auctionId, auction.seller, auction.bidder, protocolFee, creatorFee, sellerRev); } /** * @inheritdoc NFTMarketCore * @dev If an auction is found: * - If the auction is over, it will settle the auction and confirm the new seller won the auction. * - If the auction has not received a bid, it will invalidate the auction. * - If the auction is in progress, this will revert. */ function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address authorizeSeller ) internal virtual override { uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; if (auctionId != 0) { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.endTime == 0) { // The auction has not received any bids yet so it may be invalided. if (authorizeSeller != address(0) && auction.seller != authorizeSeller) { // The account trying to transfer the NFT is not the current owner. revert NFTMarketReserveAuction_Not_Matching_Seller(auction.seller); } // Remove the auction. delete nftContractToTokenIdToAuctionId[nftContract][tokenId]; delete auctionIdToAuction[auctionId]; emit ReserveAuctionInvalidated(auctionId); } else { // If the auction has ended, the highest bidder will be the new owner // and if the auction is in progress, this will revert. // `authorizeSeller != address(0)` does not apply here since an unsettled auction must go // through this path to know who the authorized seller should be. if (auction.bidder != authorizeSeller) { revert NFTMarketReserveAuction_Not_Matching_Seller(auction.bidder); } // Finalization will revert if the auction has not yet ended. _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: true }); } // The seller authorization has been confirmed. authorizeSeller = address(0); } super._transferFromEscrow(nftContract, tokenId, recipient, authorizeSeller); } /** * @inheritdoc NFTMarketCore * @dev Checks if there is an auction for this NFT before allowing the transfer to continue. */ function _transferFromEscrowIfAvailable( address nftContract, uint256 tokenId, address recipient ) internal virtual override { if (nftContractToTokenIdToAuctionId[nftContract][tokenId] == 0) { // No auction was found super._transferFromEscrowIfAvailable(nftContract, tokenId, recipient); } } /** * @inheritdoc NFTMarketCore */ function _transferToEscrow(address nftContract, uint256 tokenId) internal virtual override { uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; if (auctionId == 0) { // NFT is not in auction super._transferToEscrow(nftContract, tokenId); return; } // Using storage saves gas since most of the data is not needed ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.endTime == 0) { // Reserve price set, confirm the seller is a match if (auction.seller != msg.sender) { revert NFTMarketReserveAuction_Not_Matching_Seller(auction.seller); } } else { // Auction in progress, confirm the highest bidder is a match if (auction.bidder != msg.sender) { revert NFTMarketReserveAuction_Not_Matching_Seller(auction.bidder); } // Finalize auction but leave NFT in escrow, reverts if the auction has not ended _finalizeReserveAuction({ auctionId: auctionId, keepInEscrow: true }); } } /** * @notice Returns the minimum amount a bidder must spend to participate in an auction. * Bids must be greater than or equal to this value or they will revert. * @param auctionId The id of the auction to check. * @return minimum The minimum amount for a bid to be accepted. */ function getMinBidAmount(uint256 auctionId) external view returns (uint256 minimum) { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; if (auction.endTime == 0) { return auction.amount; } return _getMinIncrement(auction.amount); } /** * @notice Returns auction details for a given auctionId. * @param auctionId The id of the auction to lookup. */ function getReserveAuction(uint256 auctionId) external view returns (ReserveAuction memory auction) { ReserveAuctionStorage storage auctionStorage = auctionIdToAuction[auctionId]; auction = ReserveAuction( auctionStorage.nftContract, auctionStorage.tokenId, auctionStorage.seller, DURATION, EXTENSION_DURATION, auctionStorage.endTime, auctionStorage.bidder, auctionStorage.amount ); } /** * @notice Returns the auctionId for a given NFT, or 0 if no auction is found. * @dev If an auction is canceled, it will not be returned. However the auction may be over and pending finalization. * @param nftContract The address of the NFT contract. * @param tokenId The id of the NFT. * @return auctionId The id of the auction, or 0 if no auction is found. */ function getReserveAuctionIdFor(address nftContract, uint256 tokenId) external view returns (uint256 auctionId) { auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; } /** * @notice Returns the referrer for the current highest bid in the auction, or address(0). */ function getReserveAuctionBidReferrer(uint256 auctionId) external view returns (address payable referrer) { ReserveAuctionStorage storage auction = auctionIdToAuction[auctionId]; referrer = payable( address((uint160(auction.bidReferrerAddressSlot0) << 64) | uint160(auction.bidReferrerAddressSlot1)) ); } /** * @inheritdoc NFTMarketCore * @dev Returns the seller that has the given NFT in escrow for an auction, * or bubbles the call up for other considerations. */ function _getSellerFor(address nftContract, uint256 tokenId) internal view virtual override returns (address payable seller) { seller = auctionIdToAuction[nftContractToTokenIdToAuctionId[nftContract][tokenId]].seller; if (seller == address(0)) { seller = super._getSellerFor(nftContract, tokenId); } } /** * @inheritdoc NFTMarketCore */ function _isInActiveAuction(address nftContract, uint256 tokenId) internal view override returns (bool) { uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; return auctionId != 0 && auctionIdToAuction[auctionId].endTime >= block.timestamp; } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[1000] private __gap; }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "./FoundationTreasuryNode.sol"; import "./NFTMarketCore.sol"; import "./NFTMarketCreators.sol"; error SendValueWithFallbackWithdraw_No_Funds_Available(); /** * @title A mixin for sending ETH with a fallback withdraw mechanism. * @notice Attempt to send ETH and if the transfer fails or runs out of gas, store the balance * in the FETH token contract for future withdrawal instead. * @dev This mixin was recently switched to escrow funds in FETH. * Once we have confirmed all pending balances have been withdrawn, we can remove the escrow tracking here. */ abstract contract SendValueWithFallbackWithdraw is FoundationTreasuryNode, NFTMarketCore, ReentrancyGuardUpgradeable, // This is not used directly, but required for the linearization of NFTMarketFees. NFTMarketCreators { using AddressUpgradeable for address payable; /// @dev Tracks the amount of ETH that is stored in escrow for future withdrawal. mapping(address => uint256) private __gap_was_pendingWithdrawals; /** * @notice Emitted when escrowed funds are withdrawn to FETH. * @param user The account which has withdrawn ETH. * @param amount The amount of ETH which has been withdrawn. */ event WithdrawalToFETH(address indexed user, uint256 amount); /** * @dev Attempt to send a user or contract ETH and * if it fails store the amount owned for later withdrawal in FETH. */ function _sendValueWithFallbackWithdraw( address payable user, uint256 amount, uint256 gasLimit ) internal { if (amount == 0) { return; } // Cap the gas to prevent consuming all available gas to block a tx from completing successfully // solhint-disable-next-line avoid-low-level-calls (bool success, ) = user.call{ value: amount, gas: gasLimit }(""); if (!success) { // Store the funds that failed to send for the user in the FETH token feth.depositFor{ value: amount }(user); emit WithdrawalToFETH(user, amount); } } function _trySendValue( address payable user, uint256 amount, uint256 gasLimit ) internal returns (bool success) { if (amount == 0) { return false; } // Cap the gas to prevent consuming all available gas to block a tx from completing successfully // solhint-disable-next-line avoid-low-level-calls (success, ) = user.call{ value: amount, gas: gasLimit }(""); } /** * @notice This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[499] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; /** * @notice Interface for AdminRole which wraps the default admin role from * OpenZeppelin's AccessControl for easy integration. */ interface IAdminRole { function isAdmin(address account) external view returns (bool); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; /** * @notice Interface for OperatorRole which wraps a role from * OpenZeppelin's AccessControl for easy integration. */ interface IOperatorRole { function isOperator(address account) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; /** * @notice Interface for functions the market uses in FETH. */ interface IFethMarket { function depositFor(address account) external payable; function marketLockupFor(address account, uint256 amount) external payable returns (uint256 expiration); function marketWithdrawFrom(address from, uint256 amount) external; function marketWithdrawLocked( address account, uint256 expiration, uint256 amount ) external; function marketUnlockFor( address account, uint256 expiration, uint256 amount ) external; function marketChangeLockup( address unlockFrom, uint256 unlockExpiration, uint256 unlockAmount, address depositFor, uint256 depositAmount ) external payable returns (uint256 expiration); }
// 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 pragma solidity ^0.8.0; /** * From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.2.0/contracts/utils/introspection/ERC165.sol * Modified to allow checking multiple interfaces w/o checking general 165 support. */ import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @title Library to query ERC165 support. * @dev Library used to query support of an interface declared via {IERC165}. * * Note that these functions return the actual result of the query: they do not * `revert` if an interface is not supported. It is up to the caller to decide * what to do in these cases. */ library OZERC165Checker { // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; /** * @dev Returns true if `account` supports the {IERC165} interface, */ function supportsERC165(address account) internal view returns (bool) { // Any contract that implements ERC165 must explicitly indicate support of // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid return supportsERC165Interface(account, type(IERC165).interfaceId) && !supportsERC165Interface(account, _INTERFACE_ID_INVALID); } /** * @dev Returns true if `account` supports the interface defined by * `interfaceId`. Support for {IERC165} itself is queried automatically. * * See {IERC165-supportsInterface}. */ function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { // query support of both ERC165 as per the spec and support of _interfaceId return supportsERC165(account) && supportsERC165Interface(account, interfaceId); } /** * @dev Returns a boolean array where each value corresponds to the * interfaces passed in and whether they're supported or not. This allows * you to batch check interfaces for a contract where your expectation * is that some interfaces may not be supported. * * See {IERC165-supportsInterface}. * * _Available since v3.4._ */ function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) { // an array of booleans corresponding to interfaceIds and whether they're supported or not bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); // query support of ERC165 itself if (supportsERC165(account)) { // query support of each interface in interfaceIds unchecked { for (uint256 i = 0; i < interfaceIds.length; ++i) { interfaceIdsSupported[i] = supportsERC165Interface(account, interfaceIds[i]); } } } return interfaceIdsSupported; } /** * @dev Returns true if `account` supports all the interfaces defined in * `interfaceIds`. Support for {IERC165} itself is queried automatically. * * Batch-querying can lead to gas savings by skipping repeated checks for * {IERC165} support. * * See {IERC165-supportsInterface}. */ function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { // query support of ERC165 itself if (!supportsERC165(account)) { return false; } // query support of each interface in _interfaceIds unchecked { for (uint256 i = 0; i < interfaceIds.length; ++i) { if (!supportsERC165Interface(account, interfaceIds[i])) { return false; } } } // all interfaces supported return true; } /** * @notice Query if a contract implements an interface, does not check ERC165 support * @param account The address of the contract to query for support of an interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract at account indicates support of the interface with * identifier interfaceId, false otherwise * @dev Assumes that account contains a contract that supports ERC165, otherwise * the behavior of this method is undefined. This precondition can be checked * with {supportsERC165}. * Interface identification is specified in ERC-165. */ function supportsERC165Interface(address account, bytes4 interfaceId) internal view returns (bool) { bytes memory encodedParams = abi.encodeWithSelector(IERC165(account).supportsInterface.selector, interfaceId); (bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams); if (result.length < 32) return false; return success && abi.decode(result, (bool)); } }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; /** * @notice An interface for communicating fees to 3rd party marketplaces. * @dev Originally implemented in mainnet contract 0x44d6e8933f8271abcf253c72f9ed7e0e4c0323b3 */ interface IGetFees { function getFeeRecipients(uint256 id) external view returns (address payable[] memory); function getFeeBps(uint256 id) external view returns (uint256[] memory); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; interface IGetRoyalties { function getRoyalties(uint256 tokenId) external view returns (address payable[] memory recipients, uint256[] memory feesInBasisPoints); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IOwnable { /** * @dev Returns the address of the current owner. */ function owner() external view returns (address); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; /** * @notice Interface for EIP-2981: NFT Royalty Standard. * For more see: https://eips.ethereum.org/EIPS/eip-2981. */ interface IRoyaltyInfo { /// @notice Called with the sale price to determine how much royalty // is owed and to whom. /// @param _tokenId - the NFT asset queried for royalty information /// @param _salePrice - the sale price of the NFT asset specified by _tokenId /// @return receiver - address of who should be sent the royalty payment /// @return royaltyAmount - the royalty payment amount for _salePrice function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.0; interface ITokenCreator { function tokenCreator(uint256 tokenId) external view returns (address payable); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @author: manifold.xyz import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /** * @dev Royalty registry interface */ interface IRoyaltyRegistry is IERC165 { event RoyaltyOverride(address owner, address tokenAddress, address royaltyAddress); /** * Override the location of where to look up royalty information for a given token contract. * Allows for backwards compatibility and implementation of royalty logic for contracts that did not previously support them. * * @param tokenAddress - The token address you wish to override * @param royaltyAddress - The royalty override address */ function setRoyaltyLookupAddress(address tokenAddress, address royaltyAddress) external; /** * Returns royalty address location. Returns the tokenAddress by default, or the override if it exists * * @param tokenAddress - The token address you are looking up the royalty for */ function getRoyaltyLookupAddress(address tokenAddress) external view returns(address); /** * Whether or not the message sender can override the royalty address for the given token address * * @param tokenAddress - The token address you are looking up the royalty for */ function overrideAllowed(address tokenAddress) external view returns(bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } else if (error == RecoverError.InvalidSignatureV) { revert("ECDSA: invalid signature 'v' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return tryRecover(hash, r, vs); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } if (v != 27 && v != 28) { return (address(0), RecoverError.InvalidSignatureV); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
{ "optimizer": { "enabled": true, "runs": 1337 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "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":"marketProxyAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FoundationTreasuryNode_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"FoundationTreasuryNode_Caller_Not_Admin","type":"error"},{"inputs":[{"internalType":"uint256","name":"buyPrice","type":"uint256"}],"name":"NFTMarketBuyPrice_Cannot_Buy_At_Lower_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Cannot_Buy_Unset_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Cannot_Cancel_Unset_Price","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketBuyPrice_Only_Owner_Can_Cancel_Price","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketBuyPrice_Only_Owner_Can_Set_Price","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Price_Already_Set","type":"error"},{"inputs":[],"name":"NFTMarketBuyPrice_Price_Too_High","type":"error"},{"inputs":[{"internalType":"address","name":"seller","type":"address"}],"name":"NFTMarketBuyPrice_Seller_Mismatch","type":"error"},{"inputs":[],"name":"NFTMarketCore_FETH_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTMarketCore_Only_FETH_Can_Transfer_ETH","type":"error"},{"inputs":[],"name":"NFTMarketCore_Seller_Not_Found","type":"error"},{"inputs":[],"name":"NFTMarketCreators_Address_Does_Not_Support_IRoyaltyRegistry","type":"error"},{"inputs":[],"name":"NFTMarketOffer_Cannot_Be_Made_While_In_Auction","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentOfferAmount","type":"uint256"}],"name":"NFTMarketOffer_Offer_Below_Min_Amount","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"NFTMarketOffer_Offer_Expired","type":"error"},{"inputs":[{"internalType":"address","name":"currentOfferFrom","type":"address"}],"name":"NFTMarketOffer_Offer_From_Does_Not_Match","type":"error"},{"inputs":[{"internalType":"uint256","name":"minOfferAmount","type":"uint256"}],"name":"NFTMarketOffer_Offer_Must_Be_At_Least_Min_Amount","type":"error"},{"inputs":[],"name":"NFTMarketOffer_Provided_Contract_And_TokenId_Count_Must_Match","type":"error"},{"inputs":[],"name":"NFTMarketOffer_Reason_Required","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Can_Be_Offered_For_24Hrs_Max","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Proxy_Address_Is_Not_A_Contract","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Sale_Expired","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Signature_Canceled_Or_Already_Claimed","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Signature_Verification_Failed","type":"error"},{"inputs":[],"name":"NFTMarketPrivateSale_Too_Much_Value_Provided","type":"error"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"NFTMarketReserveAuction_Already_Listed","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"NFTMarketReserveAuction_Bid_Must_Be_At_Least_Min_Amount","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Admin_Cancel_Without_Reason","type":"error"},{"inputs":[{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Bid_Lower_Than_Reserve_Price","type":"error"},{"inputs":[{"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Bid_On_Ended_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Bid_On_Nonexistent_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Cancel_Nonexistent_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Finalize_Already_Settled_Auction","type":"error"},{"inputs":[{"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"NFTMarketReserveAuction_Cannot_Finalize_Auction_In_Progress","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Rebid_Over_Outstanding_Bid","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Cannot_Update_Auction_In_Progress","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxDuration","type":"uint256"}],"name":"NFTMarketReserveAuction_Exceeds_Max_Duration","type":"error"},{"inputs":[{"internalType":"uint256","name":"extensionDuration","type":"uint256"}],"name":"NFTMarketReserveAuction_Less_Than_Extension_Duration","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Must_Set_Non_Zero_Reserve_Price","type":"error"},{"inputs":[{"internalType":"address","name":"seller","type":"address"}],"name":"NFTMarketReserveAuction_Not_Matching_Seller","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NFTMarketReserveAuction_Only_Owner_Can_Update_Auction","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Price_Already_Set","type":"error"},{"inputs":[],"name":"NFTMarketReserveAuction_Too_Much_Value_Provided","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"BuyPriceAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BuyPriceCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BuyPriceInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"BuyPriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyReferrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyReferrerProtocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"buyReferrerSellerFee","type":"uint256"}],"name":"BuyReferralPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"OfferAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"OfferCanceledByAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"OfferInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiration","type":"uint256"}],"name":"OfferMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"PrivateSaleFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"bidder","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"ReserveAuctionBidPlaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"ReserveAuctionCanceledByAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"address","name":"nftContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"extensionDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reservePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":true,"internalType":"address","name":"bidder","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creatorFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sellerRev","type":"uint256"}],"name":"ReserveAuctionFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"ReserveAuctionInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"auctionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"ReserveAuctionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawalToFETH","type":"event"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"offerFrom","type":"address"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"acceptOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"nftContracts","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"string","name":"reason","type":"string"}],"name":"adminCancelOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"string","name":"reason","type":"string"}],"name":"adminCancelReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"buyFromPrivateSale","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"buyFromPrivateSaleFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"buyV2","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"cancelBuyPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"cancelReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"createReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"finalizeReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getBuyPrice","outputs":[{"internalType":"address","name":"seller","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"getFeesAndRecipients","outputs":[{"internalType":"uint256","name":"protocolFee","type":"uint256"},{"internalType":"uint256","name":"creatorRev","type":"uint256"},{"internalType":"address payable[]","name":"creatorRecipients","type":"address[]"},{"internalType":"uint256[]","name":"creatorShares","type":"uint256[]"},{"internalType":"uint256","name":"sellerRev","type":"uint256"},{"internalType":"address payable","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFethAddress","outputs":[{"internalType":"address","name":"fethAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFoundationTreasury","outputs":[{"internalType":"address payable","name":"treasuryAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getMinBidAmount","outputs":[{"internalType":"uint256","name":"minimum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getMinOfferAmount","outputs":[{"internalType":"uint256","name":"minimum","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getOffer","outputs":[{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint256","name":"expiration","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getOfferReferrer","outputs":[{"internalType":"address payable","name":"referrer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getReserveAuction","outputs":[{"components":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address payable","name":"seller","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"extensionDuration","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"address payable","name":"bidder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct NFTMarketReserveAuction.ReserveAuction","name":"auction","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"getReserveAuctionBidReferrer","outputs":[{"internalType":"address payable","name":"referrer","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getReserveAuctionIdFor","outputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoyaltyRegistry","outputs":[{"internalType":"address","name":"registry","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"makeOffer","outputs":[{"internalType":"uint256","name":"expiration","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"makeOfferV2","outputs":[{"internalType":"uint256","name":"expiration","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"name":"placeBid","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"referrer","type":"address"}],"name":"placeBidV2","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"setBuyPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"},{"internalType":"uint256","name":"reservePrice","type":"uint256"}],"name":"updateReserveAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6101206040523480156200001257600080fd5b5060405162005d7438038062005d74833981016040819052620000359162000417565b808284868862000059816001600160a01b03166200029b60201b62002ba91760201c565b620000775760405163028bba2560e61b815260040160405180910390fd5b6001600160a01b039081166080526200009e9082166200029b602090811b62002ba917901c565b620000bc576040516346b6915b60e11b815260040160405180910390fd5b6001600160a01b0390811660a052620000eb90821663220258eb60e21b620002aa602090811b62002bb817901c565b62000109576040516320af3a1f60e11b815260040160405180910390fd5b6001600160a01b031660c0526305265c00811115620001465760405163ccd285bd60e01b81526305265c0060048201526024015b60405180910390fd5b6103848110156200016f5760405163494c8c0760e11b815261038460048201526024016200013d565b60e052620001926001600160a01b0382166200029b602090811b62002ba917901c565b620001b05760405163037b60fb60e61b815260040160405180910390fd5b604080518082018252600c81526b11939113919513585c9ad95d60a21b6020918201528151808301835260018152603160f81b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527ffbcf34d9e40a15bdeb367420eb7542a501124a2eeb8d040648f646644b4baf2d818401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201526001600160a01b039390931660a0808501919091528251808503909101815260c090930190915281519101206101005250620004f49350505050565b6001600160a01b03163b151590565b6000620002b783620002d4565b8015620002cb5750620002cb83836200030c565b90505b92915050565b6000620002e9826301ffc9a760e01b6200030c565b8015620002ce575062000305826001600160e01b03196200030c565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090620003759086906200048b565b6000604051808303818686fa925050503d8060008114620003b3576040519150601f19603f3d011682016040523d82523d6000602084013e620003b8565b606091505b5091509150602081511015620003d55760009350505050620002ce565b818015620003f4575080806020019051810190620003f49190620004c9565b9695505050505050565b6001600160a01b03811681146200041457600080fd5b50565b600080600080600060a086880312156200043057600080fd5b85516200043d81620003fe565b60208701519095506200045081620003fe565b60408701519094506200046381620003fe565b6060870151608088015191945092506200047d81620003fe565b809150509295509295909350565b6000825160005b81811015620004ae576020818601810151858301520162000492565b81811115620004be576000828501525b509190910192915050565b600060208284031215620004dc57600080fd5b81518015158114620004ed57600080fd5b9392505050565b60805160a05160c05160e051610100516157cb620005a9600039600061259b0152600081816114770152818161202d01526129210152600081816106ac0152613f330152600081816101e1015281816104a901528181610b9c015281816118d301528181611a1e0152818161232501528181612a9801528181612cdf01528181612f4f0152818161368901526146610152600081816106ff0152818161084f0152818161150401526138ce01526157cb6000f3fe6080604052600436106101d15760003560e01c80637de3bd07116100f7578063ac71045e11610095578063b6aff8c111610064578063b6aff8c11461068a578063daa351d41461069d578063e5d1e723146106d0578063f7a2da23146106f057600080fd5b8063ac71045e146105ed578063af1e1de314610632578063b01ef60814610664578063b103d74f1461067757600080fd5b80639979ef45116100d15780639979ef45146104cd5780639e64ba6c146104e05780639e79b41f1461054a578063a59ac6dd146105da57600080fd5b80637de3bd07146104725780638129fc1c14610485578063895633ba1461049a57600080fd5b80634635256e1161016f578063614b151c1161013e578063614b151c1461040c5780636775d96a1461041f5780637430e0c614610432578063798bac8d1461045257600080fd5b80634635256e1461036d57806347e35740146103ac5780634ce6931a146103cc5780635d83d562146103ec57600080fd5b806321561935116101ab578063215619351461029f578063262907c5146102bf57806329e0e160146102fc5780632ab2b52b1461031c57600080fd5b806303ec16d71461023f57806306dcf7481461025f57806321506fff1461027f57600080fd5b3661023a57336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610238576040517f37de3dd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561024b57600080fd5b5061023861025a366004614f62565b610723565b34801561026b57600080fd5b5061023861027a36600461500b565b61083a565b34801561028b57600080fd5b5061023861029a3660046150a5565b610c95565b3480156102ab57600080fd5b506102386102ba3660046150d3565b610eb0565b3480156102cb57600080fd5b506102df6102da3660046150d3565b611025565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561030857600080fd5b506102386103173660046150ff565b6110a6565b34801561032857600080fd5b5061035f6103373660046150d3565b6001600160a01b03909116600090815261177660209081526040808320938352929052205490565b6040519081526020016102f3565b34801561037957600080fd5b5061038d6103883660046150d3565b611243565b604080516001600160a01b0390931683526020830191909152016102f3565b3480156103b857600080fd5b5061035f6103c73660046150a5565b6112b9565b3480156103d857600080fd5b506102386103e7366004615147565b6112f1565b3480156103f857600080fd5b5061023861040736600461517c565b6114ef565b61035f61041a3660046151c8565b61180e565b61023861042d366004615228565b611ba8565b34801561043e57600080fd5b5061023861044d3660046150a5565b611bbf565b34801561045e57600080fd5b5061023861046d366004615147565b611c77565b61035f610480366004615147565b611e97565b34801561049157600080fd5b50610238611ea6565b3480156104a657600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006102df565b6102386104db3660046150a5565b611f76565b3480156104ec57600080fd5b506102df6104fb3660046150a5565b60009081526117776020526040908190206006810154600290910154600160a01b9182900467ffffffffffffffff1691900490911b73ffffffffffffffffffffffff0000000000000000161790565b34801561055657600080fd5b5061056a6105653660046150a5565b611f82565b6040516102f391906000610100820190506001600160a01b0380845116835260208401516020840152806040850151166040840152606084015160608401526080840151608084015260a084015160a08401528060c08501511660c08401525060e083015160e083015292915050565b6102386105e8366004615147565b61207c565b3480156105f957600080fd5b5061060d6106083660046150d3565b61208e565b604080516001600160a01b0390941684526020840192909252908201526060016102f3565b34801561063e57600080fd5b5061065261064d366004615147565b612110565b6040516102f396959493929190615282565b6102386106723660046151c8565b61214e565b610238610685366004615330565b612224565b610238610698366004615395565b612731565b3480156106a957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006102df565b3480156106dc57600080fd5b5061035f6106eb3660046150d3565b612b47565b3480156106fc57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006102df565b808060000361074557604051631d4b87f360e11b815260040160405180910390fd5b60008381526117776020526040902060028101546001600160a01b03163314610797576002810154604051632600954360e21b81526001600160a01b0390911660048201526024015b60405180910390fd5b6005810154156107ba57604051635aea7c4760e01b815260040160405180910390fd5b828160070154036107f7576040517f4b669ac700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007810183905560405183815284907f0c0f2662914f0cd1e952db2aa425901cb00e7c1f507687d22cb04e836d55d9c7906020015b60405180910390a250505050565b604051630935e01b60e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906324d7806c90602401602060405180830381865afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c291906153ce565b6108df5760405163af8db33360e01b815260040160405180910390fd5b6002610b8754036109325760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556000819003610973576040517ffedbcec600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8483146109ac576040517f4716476200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015610c8657600061233760008989858181106109d0576109d06153f0565b90506020020160208101906109e59190615406565b6001600160a01b03166001600160a01b031681526020019081526020016000206000878785818110610a1957610a196153f0565b602090810292909201358352508181019290925260409081016000908120825160a081018452815463ffffffff808216835264010000000082046001600160601b031696830196909652600160801b90046fffffffffffffffffffffffffffffffff1693810193909352600101546001600160a01b0381166060840152600160a01b90049092166080820152915061233790898985818110610abd57610abd6153f0565b9050602002016020810190610ad29190615406565b6001600160a01b03166001600160a01b031681526020019081526020016000206000878785818110610b0657610b066153f0565b602090810292909201358352508101919091526040016000908120908155600101805477ffffffffffffffffffffffffffffffffffffffffffffffff1916905580514263ffffffff90911610610c7d5760608101518151602083015160405163345db49360e01b81526001600160a01b03938416600482015263ffffffff90921660248301526001600160601b031660448201527f00000000000000000000000000000000000000000000000000000000000000009091169063345db49390606401600060405180830381600087803b158015610be257600080fd5b505af1158015610bf6573d6000803e3d6000fd5b50505050858583818110610c0c57610c0c6153f0565b90506020020135888884818110610c2557610c256153f0565b9050602002016020810190610c3a9190615406565b6001600160a01b03167fd3802baab3d80ff411a2f83c8394d07877339ff8352f7c1b02fbcd70ea7cac8c8686604051610c74929190615423565b60405180910390a35b506001016109af565b50506001610b87555050505050565b6002610b875403610ce85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b878190556000828152611777602090815260409182902082516101408101845281546001600160a01b03908116825260018301549382019390935293810154808316938501849052600160a01b908190046001600160601b0316606086015260038201546080860152600482015460a0860152600582015460c0860152600682015492831660e086015290910467ffffffffffffffff16610100840152600701546101208301523314610dc3576040808201519051632600954360e21b81526001600160a01b03909116600482015260240161078e565b60c081015115610de657604051635aea7c4760e01b815260040160405180910390fd5b80516001600160a01b03166000908152611776602090815260408083208285018051855290835281842084905585845261177790925280832080546001600160a01b031916815560018101849055600281018490556003810184905560048101849055600581018490556006810180546001600160e01b0319169055600701929092558251905191830151610e7b9290612bd4565b60405182907f14b9c40404d5b41deb481f9a40b8aeb2bf4b47679b38cf757075a66ed510f7f190600090a250506001610b8755565b6002610b875403610f035760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680610f67576040517fc09f8e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381163314610fb4576040517ff049b41a0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161078e565b6001600160a01b0383166000908152611f4e60209081526040808320858452909152812055610fe4838333612bd4565b60405182906001600160a01b038516907f70c7877531c04c7d9caa8a7eca127384f04e8a6ee58b63f778ce5401d8bcae4190600090a350506001610b875550565b6001600160a01b038216600090815261233760209081526040808320848452909152812080544263ffffffff90911610156110645760009150506110a0565b80546001820154600160801b90910460201b73ffffffffffffffffffffffffffffffff0000000016600160a01b90910463ffffffff16175b9150505b92915050565b6002610b8754036110f95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038416600090815261233760209081526040808320868452909152902080544263ffffffff90911610156111715780546040517f8c9e57cf00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260240161078e565b805464010000000090046001600160601b03168211156111d25780546040517f242373610000000000000000000000000000000000000000000000000000000081526401000000009091046001600160601b0316600482015260240161078e565b60018101546001600160a01b0384811691161461122c5760018101546040517fa7d95dc30000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161078e565b6112368585612bdf565b50506001610b8755505050565b6001600160a01b038083166000908152611f4e60209081526040808320858452909152812054909116908161127b57506000196112b2565b506001600160a01b0383166000908152611f4e60209081526040808320858452909152902054600160a01b90046001600160601b03165b9250929050565b600081815261177760205260408120600581015482036112dd576007015492915050565b6112ea8160070154612e7c565b9392505050565b6002610b8754036113445760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b875580600081900361136d57604051631d4b87f360e11b815260040160405180910390fd5b600061138161138d80546001810190915590565b905061138d8585612e9e565b6001600160a01b03851660009081526117766020908152604080832087845290915290205415611413576001600160a01b038516600090815261177660209081526040808320878452909152908190205490517f7618a003000000000000000000000000000000000000000000000000000000008152600481019190915260240161078e565b6001600160a01b038516600081815261177660209081526040808320888452825280832085905584835261177782529182902080546001600160a01b03199081168517825560018201899055600282018054339216821790556007820188905583517f00000000000000000000000000000000000000000000000000000000000000008152610384938101939093529282018790526060820185905292879290917f1062dd3b35f12b4064331244d00f40c1d4831965e4285654157a2409c6217cff9060800160405180910390a450506001610b875550505050565b604051630935e01b60e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906324d7806c90602401602060405180830381865afa158015611553573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157791906153ce565b6115945760405163af8db33360e01b815260040160405180910390fd5b6002610b8754036115e75760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556000819003611628576040517ffbaca1c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526117776020908152604080832081516101408101835281546001600160a01b039081168252600183015494820194909452600282015480851693820193909352600160a01b928390046001600160601b0316606082015260038201546080820152600482015460a0820152600582015460c0820152600682015493841660e08201529190920467ffffffffffffffff16610100820152600790910154610120820181905290910361170a576040517fe3a2ab0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516001600160a01b03166000908152611776602090815260408083208285018051855290835281842084905587845261177790925280832080546001600160a01b031916815560018101849055600281018490556003810184905560048101849055600581018490556006810180546001600160e01b031916905560070192909255825190519183015161179f9290612bd4565b60e08101516001600160a01b0316156117c8576117c88160e00151826101200151614e20612eac565b837f1d56d378404d81e3fc5f3dfbf88359b8cb2ecafa73b3270c478bf7b2bdd1446984846040516117fa929190615423565b60405180910390a250506001610b87555050565b600061181b858585612fee565b1561182857506000611ba0565b611832858561305c565b15611869576040517f83a483f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516600090815261233760209081526040808320878452909152902080544263ffffffff9091161015611951576040517f4ec58ed7000000000000000000000000000000000000000000000000000000008152336004820152602481018590527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690634ec58ed790349060440160206040518083038185885af1158015611925573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061194a9190615452565b9150611a93565b80546000906119709064010000000090046001600160601b0316612e7c565b9050808510156119af576040517fe40a30e60000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b600182015482546040517f5fdec5610000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015263ffffffff821660248201526401000000009091046001600160601b03166044820152336064820152608481018790527f000000000000000000000000000000000000000000000000000000000000000090911690635fdec56190349060a40160206040518083038185885af1158015611a6a573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611a8f9190615452565b9250505b600181018054825463ffffffff8581167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909216919091176401000000006001600160601b03891602176fffffffffffffffffffffffffffffffff908116602088811c909216600160801b0217855577ffffffffffffffffffffffffffffffffffffffffffffffff19909216337fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff811691909117600160a01b92881692909202919091179092556040805187815291820185905287916001600160a01b038a16917ece0a712e4e277ac7b34942865f0de7a5629dffe0539b70423ad5ff1ed6ab42910160405180910390a4505b949350505050565b611bb786863487878787612224565b505050505050565b6002610b875403611c125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755600081815261177760205260408120600501549003611c63576040517f4b6ad8fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c6e8160006130a8565b506001610b8755565b6002610b875403611cca5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755611cdb8383836132e1565b611e8c576001600160601b03811115611d20576040517f35ec82cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038381166000908152611f4e60209081526040808320868452909152902080549091811690600160a01b90046001600160601b031683148015611d7257506001600160a01b03811615155b15611da9576040517fb6950f3600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546001600160601b038416600160a01b026001600160a01b039182161783558116611dee57611dd98585612e9e565b81546001600160a01b03191633178255611e3b565b6001600160a01b0381163314611e3b576040517f697d918e0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161078e565b336001600160a01b031684866001600160a01b03167ffcc77ea8bdcce862f43b7fb00fe6b0eb90d6aeead27d3800d9257cf7a05f9d9686604051611e8191815260200190565b60405180910390a450505b50506001610b875550565b6000611ba0848484600061180e565b600054610100900460ff16611ec15760005460ff1615611ec5565b303b155b611f375760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161078e565b600054610100900460ff16158015611f59576000805461ffff19166101011790555b611f61613346565b8015611f73576000805461ff00191690555b50565b611f7381346000612731565b611fe560405180610100016040528060006001600160a01b031681526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600081525090565b506000908152611777602090815260409182902082516101008101845281546001600160a01b03908116825260018301549382019390935260028201548316938101939093527f000000000000000000000000000000000000000000000000000000000000000060608401526103846080840152600581015460a0840152600681015490911660c08301526007015460e082015290565b612089838383600061214e565b505050565b6001600160a01b03821660009081526123376020908152604080832084845290915281208054829182914263ffffffff90911610156120d857600080600093509350935050612109565b600181015490546001600160a01b03909116935063ffffffff8116925064010000000090046001600160601b031690505b9250925092565b60008060608060008060006121258a8a6133cb565b90506121358a8a838b60006133d7565b50949f919e50929c50909a509198509650945050505050565b6001600160a01b0384166000908152611f4e6020908152604080832086845290915290208054600160a01b90046001600160601b03168310156121d15780546040517f16b5016f000000000000000000000000000000000000000000000000000000008152600160a01b9091046001600160601b0316600482015260240161078e565b80546001600160a01b0316612212576040517fda48e18400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61221d858584613585565b5050505050565b6002610b8754036122775760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755428410156122b7576040517f98dbee1b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b426202a300018411156122f6576040517f3807938200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61230087876137bd565b3485111561238e5760405163452f2b8f60e01b815233600482015234860360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063452f2b8f90604401600060405180830381600087803b15801561237157600080fd5b505af1158015612385573d6000803e3d6000fd5b505050506123c8565b348510156123c8576040517ff8c4782000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516331a9108f60e11b8152600481018790526000906001600160a01b03891690636352211e90602401602060405180830381865afa158015612410573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612434919061546b565b6001600160a01b03808a166000908152611b66602090815260408083208c84528252808320338452825280832093851683529281528282208a835281528282208983529052205490915060ff16156124b8576040517fd0be1ad300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038881166000818152611b66602090815260408083208c845282528083203380855290835281842095871684529482528083208b845282528083208a84528252808320805460ff1916600117905580517f6f5a5497fcb7364f6bad56db9aad5785b6786717424e748b8bfef6e6554cd5518184015280820194909452606084018c9052608084019490945260a083018a905260c08084018a90528451808503909101815260e0840190945283519301929092207f19010000000000000000000000000000000000000000000000000000000000006101008301527f00000000000000000000000000000000000000000000000000000000000000006101028301526101228201526101420160408051601f19818403018152919052805160209091012090506001600160a01b0382166125fa828787876137fb565b6001600160a01b03161461263a576040517f3902771600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506040516323b872dd60e01b81526001600160a01b038281166004830152336024830152604482018990528916906323b872dd90606401600060405180830381600087803b15801561268b57600080fd5b505af115801561269f573d6000803e3d6000fd5b5050505060008060006126b68b8b868c6000613823565b604080513381526020810185905290810183905260608101829052608081018c905292955090935091506001600160a01b03808616918c918e16907f6c623fa5e13aaaf28288f807e5b4f9ec6fb7ef812568e00317c552663bea918f9060a00160405180910390a450506001610b8755505050505050505050565b6002610b8754036127845760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b875560008381526117776020526040812060078101549091036127d7576040517f125197d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34831015612811576040517fe2bbc1e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058101546001600160a01b03831615158061282c57508015155b1561289a576002820180546001600160a01b0316604085901c6001600160601b0316600160a01b908102919091179091556006830180547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1667ffffffffffffffff86169092029190911790555b8060000361294d5781600701548410156128e85781600701546040517f31e6f71c00000000000000000000000000000000000000000000000000000000815260040161078e91815260200190565b81546001830154612902916001600160a01b031690613a82565b50600781018390556006810180546001600160a01b03191633179055427f00000000000000000000000000000000000000000000000000000000000000000160058201819055612a73565b4281101561298a576040517f3feeb88d0000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b6006820154336001600160a01b03909116036129d2576040517fe140576800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006129e18360070154612e7c565b905080851015612a20576040517fcd698a190000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b50600782018054600684018054928790556001600160a01b0319831633179055906001600160a01b0316426103840180841015612a6257600585018190559250825b50612a708183614e20612eac565b50505b34841115612afd5760405163452f2b8f60e01b815233600482015234850360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063452f2b8f90604401600060405180830381600087803b158015612ae457600080fd5b505af1158015612af8573d6000803e3d6000fd5b505050505b6040805185815260208101839052339187917f26ea3ebbda62eb1baef13e1c237dddd956c87f80b2801f2616d806d52557b121910160405180910390a350506001610b8755505050565b6001600160a01b038216600090815261233760209081526040808320848452909152812080544263ffffffff90911610612b9f578054612b979064010000000090046001600160601b0316612e7c565b9150506110a0565b5060019392505050565b6001600160a01b03163b151590565b6000612bc383613a8c565b80156112ea57506112ea8383613abf565b612089838383613bbd565b6001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046001600160601b03908116848901908152600160801b9093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c52979099529890945577ffffffffffffffffffffffffffffffffffffffffffffffff1990961690965591518251955193517f4dc8fb3c000000000000000000000000000000000000000000000000000000008152908716600482015294909316602485015291166044830152917f00000000000000000000000000000000000000000000000000000000000000001690634dc8fb3c90606401600060405180830381600087803b158015612d2357600080fd5b505af1158015612d37573d6000803e3d6000fd5b5050505060608101516040516323b872dd60e01b81523360048201526001600160a01b03918216602482015260448101849052908416906323b872dd90606401600060405180830381600087803b158015612d9157600080fd5b505af1925050508015612da2575060015b612db657612db68383836060015133613bf4565b6000806000612e0c86863387602001516001600160601b0316612e0789604001518a6080015173ffffffffffffffffffffffffffffffff0000000060209290921b9190911663ffffffff9091161790565b613823565b606080880151604080513381526020810187905290810185905291820183905293965091945092506001600160a01b03918216918791908916907f1cb8adb37d6d35e94cd0695ca39895b84371864713f5ca7eada52af9ff23744b906080015b60405180910390a4505050505050565b6000600a8204808203612e94576112ea83600161549e565b6112ea838261549e565b612ea88282613c00565b5050565b81600003612eb957505050565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d8060008114612f09576040519150601f19603f3d011682016040523d82523d6000602084013e612f0e565b606091505b5050905080612fe8576040517faa67c9190000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063aa67c9199085906024016000604051808303818588803b158015612f9457600080fd5b505af1158015612fa8573d6000803e3d6000fd5b5050505050836001600160a01b03167fa2201512569adb2d513531dfd69b66df50bd5cffb8c1bbe65a4611f9e1eadbd18460405161082c91815260200190565b50505050565b6001600160a01b038084166000908152611f4e60209081526040808320868452909152812080549192909116158061303657508054600160a01b90046001600160601b031683105b156130455760009150506112ea565b61305185856000613585565b506001949350505050565b6001600160a01b038216600090815261177660209081526040808320848452909152812054801580159061109c5750600090815261177760205260409020600501544211159392505050565b6000828152611777602090815260409182902082516101408101845281546001600160a01b039081168252600183015493820193909352600282015480841694820194909452600160a01b938490046001600160601b0316606082015260038201546080820152600482015460a0820152600582015460c08201819052600683015493841660e08301529390920467ffffffffffffffff1661010083015260070154610120820152904211613191578060c001516040517f3a017f6000000000000000000000000000000000000000000000000000000000815260040161078e91815260200190565b80516001600160a01b03166000908152611776602090815260408083208285015184528252808320839055858352611777909152812080546001600160a01b031916815560018101829055600281018290556003810182905560048101829055600581018290556006810180546001600160e01b0319169055600701558161322c5761322c816000015182602001518360e001516000613c6a565b600080600061327a84600001518560200151866040015187610120015188610100015167ffffffffffffffff1660408a606001516001600160601b03166001600160a01b0316901b17613823565b9250925092508360e001516001600160a01b031684604001516001600160a01b0316877f2edb0e99c6ac35be6731dab554c1d1fa1b7beb675090dbb09fb14e615aca1c4a868686604051612e6c939291909283526020830191909152604082015260600190565b6001600160a01b038316600090815261233760209081526040808320858452909152812080544263ffffffff909116108061332d5750805464010000000090046001600160601b031683115b1561333c5760009150506112ea565b6130518585612bdf565b600054610100900460ff166133c35760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161078e565b600161138d55565b60006112ea8383613d19565b600060608060008060008060008c6001600160a01b03166340c1a064619c408e6040518363ffffffff1660e01b815260040161341591815260200190565b6020604051808303818786fa9350505050801561344f575060408051601f3d908101601f1916820190925261344c9181019061546b565b60015b156134575790505b6134618d8d613d4f565b8151919850965060148b0490156134fd57816001600160a01b03168c6001600160a01b031614806134c95750875160011480156134c95750876000815181106134ac576134ac6153f0565b60200260200101516001600160a01b03168c6001600160a01b0316145b156134d857808b039550613506565b600a8b0495508b9450856134ec828d6154b6565b6134f691906154b6565b9350613506565b8b9450808b0393505b6001600160a01b038a161580159061352757506001600160a01b038a163314155b801561354557508b6001600160a01b03168a6001600160a01b031614155b80156135635750816001600160a01b03168a6001600160a01b031614155b1561356f576005810492505b82810398505050959b949a509550955095509550565b6002610b8754036135d85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038381166000908152611f4e60209081526040808320868452808352818420825180840190935280549586168352600160a01b9095046001600160601b031682840152868452909152915561363a84846137bd565b3481602001516001600160601b031611156136eb57602081015160405163452f2b8f60e01b8152336004820152346001600160601b039092169190910360248201819052906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063452f2b8f90604401600060405180830381600087803b1580156136cd57600080fd5b505af11580156136e1573d6000803e3d6000fd5b505050505061371b565b3481602001516001600160601b0316101561371b57602081015161371b9033906001600160601b0316340361442e565b6137288484336000613bf4565b600080600061374b8787866000015187602001516001600160601b031689613823565b865160408051338152602081018690529081018490526060810183905293965091945092506001600160a01b039081169188918a16907fd28c0a7dd63bc853a4e36306655da9f8c0b29ff9d0605bb976ae420e46a999309060800160405180910390a450506001610b87555050505050565b6001600160a01b0380831660009081526123376020908152604080832085845290915290206001810154909133911603612089576120898383614547565b600080600061380c878787876146f8565b91509150613819816147e5565b5095945050505050565b6000806000606080600080600061383d8d8d8d8d8d6133d7565b8681019e50929c509a509298509096509094509250905081156138c9576138678983614e2061499b565b156138c457604080516001600160a01b038b81168252602082018590526000928201929092528d918f16907f141b92fd9766c80ab120598ea2f6be9802470ec59b5446dd9bf46214ead8d08e9060600160405180910390a36138c9565b600091015b6138f67f000000000000000000000000000000000000000000000000000000000000000082614e20612eac565b8615613a655784516001811115613a3b576005811115613914575060055b6000805b8281101561397557612710878281518110613935576139356153f0565b6020026020010151111561394c5760019250613975565b86818151811061395e5761395e6153f0565b602002602001015182019150806001019050613918565b508060000361398357600191505b600060015b83811015613a00576000838983815181106139a5576139a56153f0565b60200260200101518d6139b891906154cd565b6139c291906154ec565b90506139ce818461549e565b92506139f78a83815181106139e5576139e56153f0565b60200260200101518262033450612eac565b50600101613988565b50613a3488600081518110613a1757613a176153f0565b6020026020010151828c613a2b91906154b6565b62033450612eac565b5050613a63565b613a6386600081518110613a5157613a516153f0565b60200260200101518962033450612eac565b505b613a728387614e20612eac565b5050505050955095509592505050565b612ea88282614a09565b6000613a9f826301ffc9a760e01b613abf565b80156110a05750613ab8826001600160e01b0319613abf565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090613b3b90869061550e565b6000604051808303818686fa925050503d8060008114613b77576040519150601f19603f3d011682016040523d82523d6000602084013e613b7c565b606091505b5091509150602081511015613b9757600093505050506110a0565b818015613bb3575080806020019051810190613bb391906153ce565b9695505050505050565b6001600160a01b038084166000908152611f4e602090815260408083208684529091529020541680612fe857612fe8848484614a1d565b612fe884848484614a53565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680613c36576120898383614ad7565b6001600160a01b0381163314612089576040516332f3b03360e01b81526001600160a01b038216600482015260240161078e565b6001600160a01b03811615613cab576040517f57a016b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516323b872dd60e01b81523060048201526001600160a01b038381166024830152604482018590528516906323b872dd90606401600060405180830381600087803b158015613cfb57600080fd5b505af1158015613d0f573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038083166000908152611f4e6020908152604080832085845290915290205416806110a0576112ea8383614bb5565b606080613d6c6001600160a01b03851663152a902d60e11b613abf565b15613e475760405163152a902d60e11b81526004810184905261271060248201526001600160a01b03851690632a55205a90619c409060440160408051808303818786fa93505050508015613dde575060408051601f3d908101601f19168201909252613ddb91810190615549565b60015b15613e47578015613e445760408051600180825281830190925290602080830190803683370190505093508184600081518110613e1d57613e1d6153f0565b60200260200101906001600160a01b031690816001600160a01b03168152505050506112b2565b50505b613e616001600160a01b038516635d9dd7eb60e11b613abf565b15613efb57604051635d9dd7eb60e11b8152600481018490526001600160a01b0385169063bb3bafd690619c40906024016000604051808303818786fa93505050508015613ed157506040513d6000823e601f3d908101601f19168201604052613ece91908101906156b1565b60015b15613efb5781518015801590613ee75750815181145b15613ef7575090925090506112b2565b5050505b6040517fde5488af0000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063de5488af90619c40906024016020604051808303818786fa93505050508015613f9b575060408051601f3d908101601f19168201909252613f989181019061546b565b60015b1561416b57846001600160a01b0316816001600160a01b03161461416957935083613fd66001600160a01b03821663152a902d60e11b613abf565b156140a95760405163152a902d60e11b81526004810185905261271060248201526001600160a01b03861690632a55205a90619c409060440160408051808303818786fa93505050508015614048575060408051601f3d908101601f1916820190925261404591810190615549565b60015b156140a95760408051600180825281830190925290602080830190803683370190505094508185600081518110614081576140816153f0565b60200260200101906001600160a01b031690816001600160a01b0316815250505050506112b2565b82511580156140cd57506140cd6001600160a01b038616635d9dd7eb60e11b613abf565b1561416957604051635d9dd7eb60e11b8152600481018590526001600160a01b0386169063bb3bafd690619c40906024016000604051808303818786fa9350505050801561413d57506040513d6000823e601f3d908101601f1916820160405261413a91908101906156b1565b60015b1561416957815180158015906141535750815181145b15614165575090935091506112b29050565b5050505b505b61419e6001600160a01b0385167fb779958400000000000000000000000000000000000000000000000000000000613abf565b156142d7576040517fb9c4d9fb000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0385169063b9c4d9fb90619c40906024016000604051808303818786fa9350505050801561422757506040513d6000823e601f3d908101601f191682016040526142249190810190615715565b60015b156142d757805180156142d4576040517f0ebd4c7f000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03871690630ebd4c7f90619c40906024016000604051808303818786fa935050505080156142b857506040513d6000823e601f3d908101601f191682016040526142b5919081019061574a565b60015b156142d457805182036142d2579193509091506112b29050565b505b50505b6040517f40c1a064000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b038516906340c1a06490619c40906024016020604051808303818786fa93505050508015614356575060408051601f3d908101601f191682019092526143539181019061546b565b60015b156143c45760015b604051908082528060200260200182016040528015614387578160200160208202803683370190505b509250808360008151811061439e5761439e6153f0565b60200260200101906001600160a01b031690816001600160a01b031681525050506112b2565b836001600160a01b0316638da5cb5b619c406040518263ffffffff1660e01b81526004016020604051808303818786fa93505050508015614422575060408051601f3d908101601f1916820190925261441f9181019061546b565b60015b156112b257600161435e565b8047101561447e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161078e565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146144cb576040519150601f19603f3d011682016040523d82523d6000602084013e6144d0565b606091505b50509050806120895760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161078e565b6001600160a01b0382166000908152612337602090815260408083208484529091529020544263ffffffff90911610612ea8576001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046001600160601b03908116848901908152600160801b9093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c52979099529890945577ffffffffffffffffffffffffffffffffffffffffffffffff19909616909655915182519551935163345db49360e01b8152908716600482015294909316602485015291166044830152917f0000000000000000000000000000000000000000000000000000000000000000169063345db49390606401600060405180830381600087803b1580156146a557600080fd5b505af11580156146b9573d6000803e3d6000fd5b50506040518492506001600160a01b03861691507f30c264456cbd17f5f67d7534654161414f34c0e6cc1b7500e169b7a7aea4afc090600090a3505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561472f57506000905060036147dc565b8460ff16601b1415801561474757508460ff16601c14155b1561475857506000905060046147dc565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156147ac573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166147d5576000600192509250506147dc565b9150600090505b94509492505050565b60008160048111156147f9576147f961577f565b036148015750565b60018160048111156148155761481561577f565b036148625760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161078e565b60028160048111156148765761487661577f565b036148c35760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161078e565b60038160048111156148d7576148d761577f565b0361492f5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161078e565b60048160048111156149435761494361577f565b03611f735760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161078e565b6000826000036149ad575060006112ea565b6040516001600160a01b03851690839085906000818181858888f193505050503d80600081146149f9576040519150601f19603f3d011682016040523d82523d6000602084013e6149fe565b606091505b509095945050505050565b614a138282614547565b612ea88282614bf9565b6001600160a01b038316600090815261177660209081526040808320858452909152812054900361208957612089838383614c32565b6001600160a01b038085166000908152611f4e60209081526040808320878452909152902054168015614acb57816001600160a01b0316816001600160a01b031614614abd576040516332f3b03360e01b81526001600160a01b038216600482015260240161078e565b60009150614acb8585614c9f565b61221d85858585614cf2565b6001600160a01b03821660009081526117766020908152604080832084845290915281205490819003614b0e576120898383614e94565b6000818152611777602052604081206005810154909103614b6c5760028101546001600160a01b03163314614b67576002810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b612fe8565b60068101546001600160a01b03163314614baa576006810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b612fe88260016130a8565b6001600160a01b03808316600090815261177660209081526040808320858452825280832054835261177790915290206002015416806110a0576112ea8383614ef6565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020805490911615612089576120898383614c9f565b6040516323b872dd60e01b81523060048201526001600160a01b038281166024830152604482018490528416906323b872dd90606401600060405180830381600087803b158015614c8257600080fd5b505af1158015614c96573d6000803e3d6000fd5b50505050505050565b6001600160a01b0382166000818152611f4e60209081526040808320858452909152808220829055518392917faa6271d89a385571e237d3e7254ccc7c09f68055e6e9b410ed08233a8b9a05cf91a35050565b6001600160a01b0384166000908152611776602090815260408083208684529091529020548015614e88576000818152611777602052604081206005810154909103614e36576001600160a01b03831615801590614d60575060028101546001600160a01b03848116911614155b15614d8f576002810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b6001600160a01b038616600090815261177660209081526040808320888452825280832083905584835261177790915280822080546001600160a01b031916815560018101839055600281018390556003810183905560048101839055600581018390556006810180546001600160e01b03191690556007018290555183917f5603897cc9b1e866f3f7395ffc6638776041f21c094d0b4e748ff44c407fa36291a2614e82565b60068101546001600160a01b03848116911614614e77576006810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b614e828260016130a8565b60009250505b61221d85858585613c6a565b6040516323b872dd60e01b8152336004820152306024820152604481018290526001600160a01b038316906323b872dd90606401600060405180830381600087803b158015614ee257600080fd5b505af1158015611bb7573d6000803e3d6000fd5b6040516331a9108f60e11b8152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa158015614f3e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ea919061546b565b60008060408385031215614f7557600080fd5b50508035926020909101359150565b60008083601f840112614f9657600080fd5b50813567ffffffffffffffff811115614fae57600080fd5b6020830191508360208260051b85010111156112b257600080fd5b60008083601f840112614fdb57600080fd5b50813567ffffffffffffffff811115614ff357600080fd5b6020830191508360208285010111156112b257600080fd5b6000806000806000806060878903121561502457600080fd5b863567ffffffffffffffff8082111561503c57600080fd5b6150488a838b01614f84565b9098509650602089013591508082111561506157600080fd5b61506d8a838b01614f84565b9096509450604089013591508082111561508657600080fd5b5061509389828a01614fc9565b979a9699509497509295939492505050565b6000602082840312156150b757600080fd5b5035919050565b6001600160a01b0381168114611f7357600080fd5b600080604083850312156150e657600080fd5b82356150f1816150be565b946020939093013593505050565b6000806000806080858703121561511557600080fd5b8435615120816150be565b9350602085013592506040850135615137816150be565b9396929550929360600135925050565b60008060006060848603121561515c57600080fd5b8335615167816150be565b95602085013595506040909401359392505050565b60008060006040848603121561519157600080fd5b83359250602084013567ffffffffffffffff8111156151af57600080fd5b6151bb86828701614fc9565b9497909650939450505050565b600080600080608085870312156151de57600080fd5b84356151e9816150be565b935060208501359250604085013591506060850135615207816150be565b939692955090935050565b803560ff8116811461522357600080fd5b919050565b60008060008060008060c0878903121561524157600080fd5b863561524c816150be565b9550602087013594506040870135935061526860608801615212565b92506080870135915060a087013590509295509295509295565b600060c082018883526020888185015260c0604085015281885180845260e086019150828a01935060005b818110156152d25784516001600160a01b0316835293830193918301916001016152ad565b50508481036060860152875180825290820192508188019060005b81811015615309578251855293830193918301916001016152ed565b50505050608083018590526001600160a01b03841660a08401529050979650505050505050565b600080600080600080600060e0888a03121561534b57600080fd5b8735615356816150be565b965060208801359550604088013594506060880135935061537960808901615212565b925060a0880135915060c0880135905092959891949750929550565b6000806000606084860312156153aa57600080fd5b833592506020840135915060408401356153c3816150be565b809150509250925092565b6000602082840312156153e057600080fd5b815180151581146112ea57600080fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561541857600080fd5b81356112ea816150be565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60006020828403121561546457600080fd5b5051919050565b60006020828403121561547d57600080fd5b81516112ea816150be565b634e487b7160e01b600052601160045260246000fd5b600082198211156154b1576154b1615488565b500190565b6000828210156154c8576154c8615488565b500390565b60008160001904831182151516156154e7576154e7615488565b500290565b60008261550957634e487b7160e01b600052601260045260246000fd5b500490565b6000825160005b8181101561552f5760208186018101518583015201615515565b8181111561553e576000828501525b509190910192915050565b6000806040838503121561555c57600080fd5b8251615567816150be565b6020939093015192949293505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156155b6576155b6615577565b604052919050565b600067ffffffffffffffff8211156155d8576155d8615577565b5060051b60200190565b600082601f8301126155f357600080fd5b81516020615608615603836155be565b61558d565b82815260059290921b8401810191818101908684111561562757600080fd5b8286015b8481101561564b57805161563e816150be565b835291830191830161562b565b509695505050505050565b600082601f83011261566757600080fd5b81516020615677615603836155be565b82815260059290921b8401810191818101908684111561569657600080fd5b8286015b8481101561564b578051835291830191830161569a565b600080604083850312156156c457600080fd5b825167ffffffffffffffff808211156156dc57600080fd5b6156e8868387016155e2565b935060208501519150808211156156fe57600080fd5b5061570b85828601615656565b9150509250929050565b60006020828403121561572757600080fd5b815167ffffffffffffffff81111561573e57600080fd5b61109c848285016155e2565b60006020828403121561575c57600080fd5b815167ffffffffffffffff81111561577357600080fd5b61109c84828501615656565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220042140fd23aa099c914a95e26659cefe3bfa4304f57fce37f994ce9903b4919264736f6c634300080d003300000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb600000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d0000000000000000000000000000000000000000000000000000000000015180000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f
Deployed Bytecode
0x6080604052600436106101d15760003560e01c80637de3bd07116100f7578063ac71045e11610095578063b6aff8c111610064578063b6aff8c11461068a578063daa351d41461069d578063e5d1e723146106d0578063f7a2da23146106f057600080fd5b8063ac71045e146105ed578063af1e1de314610632578063b01ef60814610664578063b103d74f1461067757600080fd5b80639979ef45116100d15780639979ef45146104cd5780639e64ba6c146104e05780639e79b41f1461054a578063a59ac6dd146105da57600080fd5b80637de3bd07146104725780638129fc1c14610485578063895633ba1461049a57600080fd5b80634635256e1161016f578063614b151c1161013e578063614b151c1461040c5780636775d96a1461041f5780637430e0c614610432578063798bac8d1461045257600080fd5b80634635256e1461036d57806347e35740146103ac5780634ce6931a146103cc5780635d83d562146103ec57600080fd5b806321561935116101ab578063215619351461029f578063262907c5146102bf57806329e0e160146102fc5780632ab2b52b1461031c57600080fd5b806303ec16d71461023f57806306dcf7481461025f57806321506fff1461027f57600080fd5b3661023a57336001600160a01b037f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504431614610238576040517f37de3dd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561024b57600080fd5b5061023861025a366004614f62565b610723565b34801561026b57600080fd5b5061023861027a36600461500b565b61083a565b34801561028b57600080fd5b5061023861029a3660046150a5565b610c95565b3480156102ab57600080fd5b506102386102ba3660046150d3565b610eb0565b3480156102cb57600080fd5b506102df6102da3660046150d3565b611025565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561030857600080fd5b506102386103173660046150ff565b6110a6565b34801561032857600080fd5b5061035f6103373660046150d3565b6001600160a01b03909116600090815261177660209081526040808320938352929052205490565b6040519081526020016102f3565b34801561037957600080fd5b5061038d6103883660046150d3565b611243565b604080516001600160a01b0390931683526020830191909152016102f3565b3480156103b857600080fd5b5061035f6103c73660046150a5565b6112b9565b3480156103d857600080fd5b506102386103e7366004615147565b6112f1565b3480156103f857600080fd5b5061023861040736600461517c565b6114ef565b61035f61041a3660046151c8565b61180e565b61023861042d366004615228565b611ba8565b34801561043e57600080fd5b5061023861044d3660046150a5565b611bbf565b34801561045e57600080fd5b5061023861046d366004615147565b611c77565b61035f610480366004615147565b611e97565b34801561049157600080fd5b50610238611ea6565b3480156104a657600080fd5b507f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436102df565b6102386104db3660046150a5565b611f76565b3480156104ec57600080fd5b506102df6104fb3660046150a5565b60009081526117776020526040908190206006810154600290910154600160a01b9182900467ffffffffffffffff1691900490911b73ffffffffffffffffffffffff0000000000000000161790565b34801561055657600080fd5b5061056a6105653660046150a5565b611f82565b6040516102f391906000610100820190506001600160a01b0380845116835260208401516020840152806040850151166040840152606084015160608401526080840151608084015260a084015160a08401528060c08501511660c08401525060e083015160e083015292915050565b6102386105e8366004615147565b61207c565b3480156105f957600080fd5b5061060d6106083660046150d3565b61208e565b604080516001600160a01b0390941684526020840192909252908201526060016102f3565b34801561063e57600080fd5b5061065261064d366004615147565b612110565b6040516102f396959493929190615282565b6102386106723660046151c8565b61214e565b610238610685366004615330565b612224565b610238610698366004615395565b612731565b3480156106a957600080fd5b507f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d6102df565b3480156106dc57600080fd5b5061035f6106eb3660046150d3565b612b47565b3480156106fc57600080fd5b507f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb66102df565b808060000361074557604051631d4b87f360e11b815260040160405180910390fd5b60008381526117776020526040902060028101546001600160a01b03163314610797576002810154604051632600954360e21b81526001600160a01b0390911660048201526024015b60405180910390fd5b6005810154156107ba57604051635aea7c4760e01b815260040160405180910390fd5b828160070154036107f7576040517f4b669ac700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6007810183905560405183815284907f0c0f2662914f0cd1e952db2aa425901cb00e7c1f507687d22cb04e836d55d9c7906020015b60405180910390a250505050565b604051630935e01b60e21b81523360048201527f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb66001600160a01b0316906324d7806c90602401602060405180830381865afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c291906153ce565b6108df5760405163af8db33360e01b815260040160405180910390fd5b6002610b8754036109325760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556000819003610973576040517ffedbcec600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8483146109ac576040517f4716476200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015610c8657600061233760008989858181106109d0576109d06153f0565b90506020020160208101906109e59190615406565b6001600160a01b03166001600160a01b031681526020019081526020016000206000878785818110610a1957610a196153f0565b602090810292909201358352508181019290925260409081016000908120825160a081018452815463ffffffff808216835264010000000082046001600160601b031696830196909652600160801b90046fffffffffffffffffffffffffffffffff1693810193909352600101546001600160a01b0381166060840152600160a01b90049092166080820152915061233790898985818110610abd57610abd6153f0565b9050602002016020810190610ad29190615406565b6001600160a01b03166001600160a01b031681526020019081526020016000206000878785818110610b0657610b066153f0565b602090810292909201358352508101919091526040016000908120908155600101805477ffffffffffffffffffffffffffffffffffffffffffffffff1916905580514263ffffffff90911610610c7d5760608101518151602083015160405163345db49360e01b81526001600160a01b03938416600482015263ffffffff90921660248301526001600160601b031660448201527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504439091169063345db49390606401600060405180830381600087803b158015610be257600080fd5b505af1158015610bf6573d6000803e3d6000fd5b50505050858583818110610c0c57610c0c6153f0565b90506020020135888884818110610c2557610c256153f0565b9050602002016020810190610c3a9190615406565b6001600160a01b03167fd3802baab3d80ff411a2f83c8394d07877339ff8352f7c1b02fbcd70ea7cac8c8686604051610c74929190615423565b60405180910390a35b506001016109af565b50506001610b87555050505050565b6002610b875403610ce85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b878190556000828152611777602090815260409182902082516101408101845281546001600160a01b03908116825260018301549382019390935293810154808316938501849052600160a01b908190046001600160601b0316606086015260038201546080860152600482015460a0860152600582015460c0860152600682015492831660e086015290910467ffffffffffffffff16610100840152600701546101208301523314610dc3576040808201519051632600954360e21b81526001600160a01b03909116600482015260240161078e565b60c081015115610de657604051635aea7c4760e01b815260040160405180910390fd5b80516001600160a01b03166000908152611776602090815260408083208285018051855290835281842084905585845261177790925280832080546001600160a01b031916815560018101849055600281018490556003810184905560048101849055600581018490556006810180546001600160e01b0319169055600701929092558251905191830151610e7b9290612bd4565b60405182907f14b9c40404d5b41deb481f9a40b8aeb2bf4b47679b38cf757075a66ed510f7f190600090a250506001610b8755565b6002610b875403610f035760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680610f67576040517fc09f8e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381163314610fb4576040517ff049b41a0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161078e565b6001600160a01b0383166000908152611f4e60209081526040808320858452909152812055610fe4838333612bd4565b60405182906001600160a01b038516907f70c7877531c04c7d9caa8a7eca127384f04e8a6ee58b63f778ce5401d8bcae4190600090a350506001610b875550565b6001600160a01b038216600090815261233760209081526040808320848452909152812080544263ffffffff90911610156110645760009150506110a0565b80546001820154600160801b90910460201b73ffffffffffffffffffffffffffffffff0000000016600160a01b90910463ffffffff16175b9150505b92915050565b6002610b8754036110f95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038416600090815261233760209081526040808320868452909152902080544263ffffffff90911610156111715780546040517f8c9e57cf00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260240161078e565b805464010000000090046001600160601b03168211156111d25780546040517f242373610000000000000000000000000000000000000000000000000000000081526401000000009091046001600160601b0316600482015260240161078e565b60018101546001600160a01b0384811691161461122c5760018101546040517fa7d95dc30000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161078e565b6112368585612bdf565b50506001610b8755505050565b6001600160a01b038083166000908152611f4e60209081526040808320858452909152812054909116908161127b57506000196112b2565b506001600160a01b0383166000908152611f4e60209081526040808320858452909152902054600160a01b90046001600160601b03165b9250929050565b600081815261177760205260408120600581015482036112dd576007015492915050565b6112ea8160070154612e7c565b9392505050565b6002610b8754036113445760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b875580600081900361136d57604051631d4b87f360e11b815260040160405180910390fd5b600061138161138d80546001810190915590565b905061138d8585612e9e565b6001600160a01b03851660009081526117766020908152604080832087845290915290205415611413576001600160a01b038516600090815261177660209081526040808320878452909152908190205490517f7618a003000000000000000000000000000000000000000000000000000000008152600481019190915260240161078e565b6001600160a01b038516600081815261177660209081526040808320888452825280832085905584835261177782529182902080546001600160a01b03199081168517825560018201899055600282018054339216821790556007820188905583517f00000000000000000000000000000000000000000000000000000000000151808152610384938101939093529282018790526060820185905292879290917f1062dd3b35f12b4064331244d00f40c1d4831965e4285654157a2409c6217cff9060800160405180910390a450506001610b875550505050565b604051630935e01b60e21b81523360048201527f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb66001600160a01b0316906324d7806c90602401602060405180830381865afa158015611553573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157791906153ce565b6115945760405163af8db33360e01b815260040160405180910390fd5b6002610b8754036115e75760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556000819003611628576040517ffbaca1c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526117776020908152604080832081516101408101835281546001600160a01b039081168252600183015494820194909452600282015480851693820193909352600160a01b928390046001600160601b0316606082015260038201546080820152600482015460a0820152600582015460c0820152600682015493841660e08201529190920467ffffffffffffffff16610100820152600790910154610120820181905290910361170a576040517fe3a2ab0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80516001600160a01b03166000908152611776602090815260408083208285018051855290835281842084905587845261177790925280832080546001600160a01b031916815560018101849055600281018490556003810184905560048101849055600581018490556006810180546001600160e01b031916905560070192909255825190519183015161179f9290612bd4565b60e08101516001600160a01b0316156117c8576117c88160e00151826101200151614e20612eac565b837f1d56d378404d81e3fc5f3dfbf88359b8cb2ecafa73b3270c478bf7b2bdd1446984846040516117fa929190615423565b60405180910390a250506001610b87555050565b600061181b858585612fee565b1561182857506000611ba0565b611832858561305c565b15611869576040517f83a483f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516600090815261233760209081526040808320878452909152902080544263ffffffff9091161015611951576040517f4ec58ed7000000000000000000000000000000000000000000000000000000008152336004820152602481018590527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436001600160a01b031690634ec58ed790349060440160206040518083038185885af1158015611925573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061194a9190615452565b9150611a93565b80546000906119709064010000000090046001600160601b0316612e7c565b9050808510156119af576040517fe40a30e60000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b600182015482546040517f5fdec5610000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015263ffffffff821660248201526401000000009091046001600160601b03166044820152336064820152608481018790527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d5044390911690635fdec56190349060a40160206040518083038185885af1158015611a6a573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611a8f9190615452565b9250505b600181018054825463ffffffff8581167fffffffffffffffffffffffffffffffff00000000000000000000000000000000909216919091176401000000006001600160601b03891602176fffffffffffffffffffffffffffffffff908116602088811c909216600160801b0217855577ffffffffffffffffffffffffffffffffffffffffffffffff19909216337fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff811691909117600160a01b92881692909202919091179092556040805187815291820185905287916001600160a01b038a16917ece0a712e4e277ac7b34942865f0de7a5629dffe0539b70423ad5ff1ed6ab42910160405180910390a4505b949350505050565b611bb786863487878787612224565b505050505050565b6002610b875403611c125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755600081815261177760205260408120600501549003611c63576040517f4b6ad8fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c6e8160006130a8565b506001610b8755565b6002610b875403611cca5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755611cdb8383836132e1565b611e8c576001600160601b03811115611d20576040517f35ec82cb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038381166000908152611f4e60209081526040808320868452909152902080549091811690600160a01b90046001600160601b031683148015611d7257506001600160a01b03811615155b15611da9576040517fb6950f3600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546001600160601b038416600160a01b026001600160a01b039182161783558116611dee57611dd98585612e9e565b81546001600160a01b03191633178255611e3b565b6001600160a01b0381163314611e3b576040517f697d918e0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260240161078e565b336001600160a01b031684866001600160a01b03167ffcc77ea8bdcce862f43b7fb00fe6b0eb90d6aeead27d3800d9257cf7a05f9d9686604051611e8191815260200190565b60405180910390a450505b50506001610b875550565b6000611ba0848484600061180e565b600054610100900460ff16611ec15760005460ff1615611ec5565b303b155b611f375760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840161078e565b600054610100900460ff16158015611f59576000805461ffff19166101011790555b611f61613346565b8015611f73576000805461ff00191690555b50565b611f7381346000612731565b611fe560405180610100016040528060006001600160a01b031681526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600081525090565b506000908152611777602090815260409182902082516101008101845281546001600160a01b03908116825260018301549382019390935260028201548316938101939093527f000000000000000000000000000000000000000000000000000000000001518060608401526103846080840152600581015460a0840152600681015490911660c08301526007015460e082015290565b612089838383600061214e565b505050565b6001600160a01b03821660009081526123376020908152604080832084845290915281208054829182914263ffffffff90911610156120d857600080600093509350935050612109565b600181015490546001600160a01b03909116935063ffffffff8116925064010000000090046001600160601b031690505b9250925092565b60008060608060008060006121258a8a6133cb565b90506121358a8a838b60006133d7565b50949f919e50929c50909a509198509650945050505050565b6001600160a01b0384166000908152611f4e6020908152604080832086845290915290208054600160a01b90046001600160601b03168310156121d15780546040517f16b5016f000000000000000000000000000000000000000000000000000000008152600160a01b9091046001600160601b0316600482015260240161078e565b80546001600160a01b0316612212576040517fda48e18400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61221d858584613585565b5050505050565b6002610b8754036122775760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b8755428410156122b7576040517f98dbee1b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b426202a300018411156122f6576040517f3807938200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61230087876137bd565b3485111561238e5760405163452f2b8f60e01b815233600482015234860360248201527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436001600160a01b03169063452f2b8f90604401600060405180830381600087803b15801561237157600080fd5b505af1158015612385573d6000803e3d6000fd5b505050506123c8565b348510156123c8576040517ff8c4782000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516331a9108f60e11b8152600481018790526000906001600160a01b03891690636352211e90602401602060405180830381865afa158015612410573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612434919061546b565b6001600160a01b03808a166000908152611b66602090815260408083208c84528252808320338452825280832093851683529281528282208a835281528282208983529052205490915060ff16156124b8576040517fd0be1ad300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038881166000818152611b66602090815260408083208c845282528083203380855290835281842095871684529482528083208b845282528083208a84528252808320805460ff1916600117905580517f6f5a5497fcb7364f6bad56db9aad5785b6786717424e748b8bfef6e6554cd5518184015280820194909452606084018c9052608084019490945260a083018a905260c08084018a90528451808503909101815260e0840190945283519301929092207f19010000000000000000000000000000000000000000000000000000000000006101008301527fa4c523e6efc3db1cacdbf55085f995f1433e482eb9b0634e8c094e0ef60eb5326101028301526101228201526101420160408051601f19818403018152919052805160209091012090506001600160a01b0382166125fa828787876137fb565b6001600160a01b03161461263a576040517f3902771600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506040516323b872dd60e01b81526001600160a01b038281166004830152336024830152604482018990528916906323b872dd90606401600060405180830381600087803b15801561268b57600080fd5b505af115801561269f573d6000803e3d6000fd5b5050505060008060006126b68b8b868c6000613823565b604080513381526020810185905290810183905260608101829052608081018c905292955090935091506001600160a01b03808616918c918e16907f6c623fa5e13aaaf28288f807e5b4f9ec6fb7ef812568e00317c552663bea918f9060a00160405180910390a450506001610b8755505050505050505050565b6002610b8754036127845760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b875560008381526117776020526040812060078101549091036127d7576040517f125197d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34831015612811576040517fe2bbc1e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058101546001600160a01b03831615158061282c57508015155b1561289a576002820180546001600160a01b0316604085901c6001600160601b0316600160a01b908102919091179091556006830180547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1667ffffffffffffffff86169092029190911790555b8060000361294d5781600701548410156128e85781600701546040517f31e6f71c00000000000000000000000000000000000000000000000000000000815260040161078e91815260200190565b81546001830154612902916001600160a01b031690613a82565b50600781018390556006810180546001600160a01b03191633179055427f00000000000000000000000000000000000000000000000000000000000151800160058201819055612a73565b4281101561298a576040517f3feeb88d0000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b6006820154336001600160a01b03909116036129d2576040517fe140576800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006129e18360070154612e7c565b905080851015612a20576040517fcd698a190000000000000000000000000000000000000000000000000000000081526004810182905260240161078e565b50600782018054600684018054928790556001600160a01b0319831633179055906001600160a01b0316426103840180841015612a6257600585018190559250825b50612a708183614e20612eac565b50505b34841115612afd5760405163452f2b8f60e01b815233600482015234850360248201527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504436001600160a01b03169063452f2b8f90604401600060405180830381600087803b158015612ae457600080fd5b505af1158015612af8573d6000803e3d6000fd5b505050505b6040805185815260208101839052339187917f26ea3ebbda62eb1baef13e1c237dddd956c87f80b2801f2616d806d52557b121910160405180910390a350506001610b8755505050565b6001600160a01b038216600090815261233760209081526040808320848452909152812080544263ffffffff90911610612b9f578054612b979064010000000090046001600160601b0316612e7c565b9150506110a0565b5060019392505050565b6001600160a01b03163b151590565b6000612bc383613a8c565b80156112ea57506112ea8383613abf565b612089838383613bbd565b6001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046001600160601b03908116848901908152600160801b9093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c52979099529890945577ffffffffffffffffffffffffffffffffffffffffffffffff1990961690965591518251955193517f4dc8fb3c000000000000000000000000000000000000000000000000000000008152908716600482015294909316602485015291166044830152917f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d504431690634dc8fb3c90606401600060405180830381600087803b158015612d2357600080fd5b505af1158015612d37573d6000803e3d6000fd5b5050505060608101516040516323b872dd60e01b81523360048201526001600160a01b03918216602482015260448101849052908416906323b872dd90606401600060405180830381600087803b158015612d9157600080fd5b505af1925050508015612da2575060015b612db657612db68383836060015133613bf4565b6000806000612e0c86863387602001516001600160601b0316612e0789604001518a6080015173ffffffffffffffffffffffffffffffff0000000060209290921b9190911663ffffffff9091161790565b613823565b606080880151604080513381526020810187905290810185905291820183905293965091945092506001600160a01b03918216918791908916907f1cb8adb37d6d35e94cd0695ca39895b84371864713f5ca7eada52af9ff23744b906080015b60405180910390a4505050505050565b6000600a8204808203612e94576112ea83600161549e565b6112ea838261549e565b612ea88282613c00565b5050565b81600003612eb957505050565b6000836001600160a01b0316838390604051600060405180830381858888f193505050503d8060008114612f09576040519150601f19603f3d011682016040523d82523d6000602084013e612f0e565b606091505b5050905080612fe8576040517faa67c9190000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063aa67c9199085906024016000604051808303818588803b158015612f9457600080fd5b505af1158015612fa8573d6000803e3d6000fd5b5050505050836001600160a01b03167fa2201512569adb2d513531dfd69b66df50bd5cffb8c1bbe65a4611f9e1eadbd18460405161082c91815260200190565b50505050565b6001600160a01b038084166000908152611f4e60209081526040808320868452909152812080549192909116158061303657508054600160a01b90046001600160601b031683105b156130455760009150506112ea565b61305185856000613585565b506001949350505050565b6001600160a01b038216600090815261177660209081526040808320848452909152812054801580159061109c5750600090815261177760205260409020600501544211159392505050565b6000828152611777602090815260409182902082516101408101845281546001600160a01b039081168252600183015493820193909352600282015480841694820194909452600160a01b938490046001600160601b0316606082015260038201546080820152600482015460a0820152600582015460c08201819052600683015493841660e08301529390920467ffffffffffffffff1661010083015260070154610120820152904211613191578060c001516040517f3a017f6000000000000000000000000000000000000000000000000000000000815260040161078e91815260200190565b80516001600160a01b03166000908152611776602090815260408083208285015184528252808320839055858352611777909152812080546001600160a01b031916815560018101829055600281018290556003810182905560048101829055600581018290556006810180546001600160e01b0319169055600701558161322c5761322c816000015182602001518360e001516000613c6a565b600080600061327a84600001518560200151866040015187610120015188610100015167ffffffffffffffff1660408a606001516001600160601b03166001600160a01b0316901b17613823565b9250925092508360e001516001600160a01b031684604001516001600160a01b0316877f2edb0e99c6ac35be6731dab554c1d1fa1b7beb675090dbb09fb14e615aca1c4a868686604051612e6c939291909283526020830191909152604082015260600190565b6001600160a01b038316600090815261233760209081526040808320858452909152812080544263ffffffff909116108061332d5750805464010000000090046001600160601b031683115b1561333c5760009150506112ea565b6130518585612bdf565b600054610100900460ff166133c35760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840161078e565b600161138d55565b60006112ea8383613d19565b600060608060008060008060008c6001600160a01b03166340c1a064619c408e6040518363ffffffff1660e01b815260040161341591815260200190565b6020604051808303818786fa9350505050801561344f575060408051601f3d908101601f1916820190925261344c9181019061546b565b60015b156134575790505b6134618d8d613d4f565b8151919850965060148b0490156134fd57816001600160a01b03168c6001600160a01b031614806134c95750875160011480156134c95750876000815181106134ac576134ac6153f0565b60200260200101516001600160a01b03168c6001600160a01b0316145b156134d857808b039550613506565b600a8b0495508b9450856134ec828d6154b6565b6134f691906154b6565b9350613506565b8b9450808b0393505b6001600160a01b038a161580159061352757506001600160a01b038a163314155b801561354557508b6001600160a01b03168a6001600160a01b031614155b80156135635750816001600160a01b03168a6001600160a01b031614155b1561356f576005810492505b82810398505050959b949a509550955095509550565b6002610b8754036135d85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161078e565b6002610b87556001600160a01b038381166000908152611f4e60209081526040808320868452808352818420825180840190935280549586168352600160a01b9095046001600160601b031682840152868452909152915561363a84846137bd565b3481602001516001600160601b031611156136eb57602081015160405163452f2b8f60e01b8152336004820152346001600160601b039092169190910360248201819052906001600160a01b037f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063452f2b8f90604401600060405180830381600087803b1580156136cd57600080fd5b505af11580156136e1573d6000803e3d6000fd5b505050505061371b565b3481602001516001600160601b0316101561371b57602081015161371b9033906001600160601b0316340361442e565b6137288484336000613bf4565b600080600061374b8787866000015187602001516001600160601b031689613823565b865160408051338152602081018690529081018490526060810183905293965091945092506001600160a01b039081169188918a16907fd28c0a7dd63bc853a4e36306655da9f8c0b29ff9d0605bb976ae420e46a999309060800160405180910390a450506001610b87555050505050565b6001600160a01b0380831660009081526123376020908152604080832085845290915290206001810154909133911603612089576120898383614547565b600080600061380c878787876146f8565b91509150613819816147e5565b5095945050505050565b6000806000606080600080600061383d8d8d8d8d8d6133d7565b8681019e50929c509a509298509096509094509250905081156138c9576138678983614e2061499b565b156138c457604080516001600160a01b038b81168252602082018590526000928201929092528d918f16907f141b92fd9766c80ab120598ea2f6be9802470ec59b5446dd9bf46214ead8d08e9060600160405180910390a36138c9565b600091015b6138f67f00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb682614e20612eac565b8615613a655784516001811115613a3b576005811115613914575060055b6000805b8281101561397557612710878281518110613935576139356153f0565b6020026020010151111561394c5760019250613975565b86818151811061395e5761395e6153f0565b602002602001015182019150806001019050613918565b508060000361398357600191505b600060015b83811015613a00576000838983815181106139a5576139a56153f0565b60200260200101518d6139b891906154cd565b6139c291906154ec565b90506139ce818461549e565b92506139f78a83815181106139e5576139e56153f0565b60200260200101518262033450612eac565b50600101613988565b50613a3488600081518110613a1757613a176153f0565b6020026020010151828c613a2b91906154b6565b62033450612eac565b5050613a63565b613a6386600081518110613a5157613a516153f0565b60200260200101518962033450612eac565b505b613a728387614e20612eac565b5050505050955095509592505050565b612ea88282614a09565b6000613a9f826301ffc9a760e01b613abf565b80156110a05750613ab8826001600160e01b0319613abf565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090613b3b90869061550e565b6000604051808303818686fa925050503d8060008114613b77576040519150601f19603f3d011682016040523d82523d6000602084013e613b7c565b606091505b5091509150602081511015613b9757600093505050506110a0565b818015613bb3575080806020019051810190613bb391906153ce565b9695505050505050565b6001600160a01b038084166000908152611f4e602090815260408083208684529091529020541680612fe857612fe8848484614a1d565b612fe884848484614a53565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020541680613c36576120898383614ad7565b6001600160a01b0381163314612089576040516332f3b03360e01b81526001600160a01b038216600482015260240161078e565b6001600160a01b03811615613cab576040517f57a016b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516323b872dd60e01b81523060048201526001600160a01b038381166024830152604482018590528516906323b872dd90606401600060405180830381600087803b158015613cfb57600080fd5b505af1158015613d0f573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038083166000908152611f4e6020908152604080832085845290915290205416806110a0576112ea8383614bb5565b606080613d6c6001600160a01b03851663152a902d60e11b613abf565b15613e475760405163152a902d60e11b81526004810184905261271060248201526001600160a01b03851690632a55205a90619c409060440160408051808303818786fa93505050508015613dde575060408051601f3d908101601f19168201909252613ddb91810190615549565b60015b15613e47578015613e445760408051600180825281830190925290602080830190803683370190505093508184600081518110613e1d57613e1d6153f0565b60200260200101906001600160a01b031690816001600160a01b03168152505050506112b2565b50505b613e616001600160a01b038516635d9dd7eb60e11b613abf565b15613efb57604051635d9dd7eb60e11b8152600481018490526001600160a01b0385169063bb3bafd690619c40906024016000604051808303818786fa93505050508015613ed157506040513d6000823e601f3d908101601f19168201604052613ece91908101906156b1565b60015b15613efb5781518015801590613ee75750815181145b15613ef7575090925090506112b2565b5050505b6040517fde5488af0000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301527f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d169063de5488af90619c40906024016020604051808303818786fa93505050508015613f9b575060408051601f3d908101601f19168201909252613f989181019061546b565b60015b1561416b57846001600160a01b0316816001600160a01b03161461416957935083613fd66001600160a01b03821663152a902d60e11b613abf565b156140a95760405163152a902d60e11b81526004810185905261271060248201526001600160a01b03861690632a55205a90619c409060440160408051808303818786fa93505050508015614048575060408051601f3d908101601f1916820190925261404591810190615549565b60015b156140a95760408051600180825281830190925290602080830190803683370190505094508185600081518110614081576140816153f0565b60200260200101906001600160a01b031690816001600160a01b0316815250505050506112b2565b82511580156140cd57506140cd6001600160a01b038616635d9dd7eb60e11b613abf565b1561416957604051635d9dd7eb60e11b8152600481018590526001600160a01b0386169063bb3bafd690619c40906024016000604051808303818786fa9350505050801561413d57506040513d6000823e601f3d908101601f1916820160405261413a91908101906156b1565b60015b1561416957815180158015906141535750815181145b15614165575090935091506112b29050565b5050505b505b61419e6001600160a01b0385167fb779958400000000000000000000000000000000000000000000000000000000613abf565b156142d7576040517fb9c4d9fb000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0385169063b9c4d9fb90619c40906024016000604051808303818786fa9350505050801561422757506040513d6000823e601f3d908101601f191682016040526142249190810190615715565b60015b156142d757805180156142d4576040517f0ebd4c7f000000000000000000000000000000000000000000000000000000008152600481018690526001600160a01b03871690630ebd4c7f90619c40906024016000604051808303818786fa935050505080156142b857506040513d6000823e601f3d908101601f191682016040526142b5919081019061574a565b60015b156142d457805182036142d2579193509091506112b29050565b505b50505b6040517f40c1a064000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b038516906340c1a06490619c40906024016020604051808303818786fa93505050508015614356575060408051601f3d908101601f191682019092526143539181019061546b565b60015b156143c45760015b604051908082528060200260200182016040528015614387578160200160208202803683370190505b509250808360008151811061439e5761439e6153f0565b60200260200101906001600160a01b031690816001600160a01b031681525050506112b2565b836001600160a01b0316638da5cb5b619c406040518263ffffffff1660e01b81526004016020604051808303818786fa93505050508015614422575060408051601f3d908101601f1916820190925261441f9181019061546b565b60015b156112b257600161435e565b8047101561447e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161078e565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146144cb576040519150601f19603f3d011682016040523d82523d6000602084013e6144d0565b606091505b50509050806120895760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161078e565b6001600160a01b0382166000908152612337602090815260408083208484529091529020544263ffffffff90911610612ea8576001600160a01b03828116600090815261233760209081526040808320858452808352818420825160a081018452815463ffffffff808216835264010000000082046001600160601b03908116848901908152600160801b9093046fffffffffffffffffffffffffffffffff1684880152600185018054808c1660608701908152600160a01b8204851660808801528d8c52979099529890945577ffffffffffffffffffffffffffffffffffffffffffffffff19909616909655915182519551935163345db49360e01b8152908716600482015294909316602485015291166044830152917f00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443169063345db49390606401600060405180830381600087803b1580156146a557600080fd5b505af11580156146b9573d6000803e3d6000fd5b50506040518492506001600160a01b03861691507f30c264456cbd17f5f67d7534654161414f34c0e6cc1b7500e169b7a7aea4afc090600090a3505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561472f57506000905060036147dc565b8460ff16601b1415801561474757508460ff16601c14155b1561475857506000905060046147dc565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156147ac573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166147d5576000600192509250506147dc565b9150600090505b94509492505050565b60008160048111156147f9576147f961577f565b036148015750565b60018160048111156148155761481561577f565b036148625760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161078e565b60028160048111156148765761487661577f565b036148c35760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161078e565b60038160048111156148d7576148d761577f565b0361492f5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161078e565b60048160048111156149435761494361577f565b03611f735760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b606482015260840161078e565b6000826000036149ad575060006112ea565b6040516001600160a01b03851690839085906000818181858888f193505050503d80600081146149f9576040519150601f19603f3d011682016040523d82523d6000602084013e6149fe565b606091505b509095945050505050565b614a138282614547565b612ea88282614bf9565b6001600160a01b038316600090815261177660209081526040808320858452909152812054900361208957612089838383614c32565b6001600160a01b038085166000908152611f4e60209081526040808320878452909152902054168015614acb57816001600160a01b0316816001600160a01b031614614abd576040516332f3b03360e01b81526001600160a01b038216600482015260240161078e565b60009150614acb8585614c9f565b61221d85858585614cf2565b6001600160a01b03821660009081526117766020908152604080832084845290915281205490819003614b0e576120898383614e94565b6000818152611777602052604081206005810154909103614b6c5760028101546001600160a01b03163314614b67576002810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b612fe8565b60068101546001600160a01b03163314614baa576006810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b612fe88260016130a8565b6001600160a01b03808316600090815261177660209081526040808320858452825280832054835261177790915290206002015416806110a0576112ea8383614ef6565b6001600160a01b038083166000908152611f4e602090815260408083208584529091529020805490911615612089576120898383614c9f565b6040516323b872dd60e01b81523060048201526001600160a01b038281166024830152604482018490528416906323b872dd90606401600060405180830381600087803b158015614c8257600080fd5b505af1158015614c96573d6000803e3d6000fd5b50505050505050565b6001600160a01b0382166000818152611f4e60209081526040808320858452909152808220829055518392917faa6271d89a385571e237d3e7254ccc7c09f68055e6e9b410ed08233a8b9a05cf91a35050565b6001600160a01b0384166000908152611776602090815260408083208684529091529020548015614e88576000818152611777602052604081206005810154909103614e36576001600160a01b03831615801590614d60575060028101546001600160a01b03848116911614155b15614d8f576002810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b6001600160a01b038616600090815261177660209081526040808320888452825280832083905584835261177790915280822080546001600160a01b031916815560018101839055600281018390556003810183905560048101839055600581018390556006810180546001600160e01b03191690556007018290555183917f5603897cc9b1e866f3f7395ffc6638776041f21c094d0b4e748ff44c407fa36291a2614e82565b60068101546001600160a01b03848116911614614e77576006810154604051637322937760e11b81526001600160a01b03909116600482015260240161078e565b614e828260016130a8565b60009250505b61221d85858585613c6a565b6040516323b872dd60e01b8152336004820152306024820152604481018290526001600160a01b038316906323b872dd90606401600060405180830381600087803b158015614ee257600080fd5b505af1158015611bb7573d6000803e3d6000fd5b6040516331a9108f60e11b8152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa158015614f3e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ea919061546b565b60008060408385031215614f7557600080fd5b50508035926020909101359150565b60008083601f840112614f9657600080fd5b50813567ffffffffffffffff811115614fae57600080fd5b6020830191508360208260051b85010111156112b257600080fd5b60008083601f840112614fdb57600080fd5b50813567ffffffffffffffff811115614ff357600080fd5b6020830191508360208285010111156112b257600080fd5b6000806000806000806060878903121561502457600080fd5b863567ffffffffffffffff8082111561503c57600080fd5b6150488a838b01614f84565b9098509650602089013591508082111561506157600080fd5b61506d8a838b01614f84565b9096509450604089013591508082111561508657600080fd5b5061509389828a01614fc9565b979a9699509497509295939492505050565b6000602082840312156150b757600080fd5b5035919050565b6001600160a01b0381168114611f7357600080fd5b600080604083850312156150e657600080fd5b82356150f1816150be565b946020939093013593505050565b6000806000806080858703121561511557600080fd5b8435615120816150be565b9350602085013592506040850135615137816150be565b9396929550929360600135925050565b60008060006060848603121561515c57600080fd5b8335615167816150be565b95602085013595506040909401359392505050565b60008060006040848603121561519157600080fd5b83359250602084013567ffffffffffffffff8111156151af57600080fd5b6151bb86828701614fc9565b9497909650939450505050565b600080600080608085870312156151de57600080fd5b84356151e9816150be565b935060208501359250604085013591506060850135615207816150be565b939692955090935050565b803560ff8116811461522357600080fd5b919050565b60008060008060008060c0878903121561524157600080fd5b863561524c816150be565b9550602087013594506040870135935061526860608801615212565b92506080870135915060a087013590509295509295509295565b600060c082018883526020888185015260c0604085015281885180845260e086019150828a01935060005b818110156152d25784516001600160a01b0316835293830193918301916001016152ad565b50508481036060860152875180825290820192508188019060005b81811015615309578251855293830193918301916001016152ed565b50505050608083018590526001600160a01b03841660a08401529050979650505050505050565b600080600080600080600060e0888a03121561534b57600080fd5b8735615356816150be565b965060208801359550604088013594506060880135935061537960808901615212565b925060a0880135915060c0880135905092959891949750929550565b6000806000606084860312156153aa57600080fd5b833592506020840135915060408401356153c3816150be565b809150509250925092565b6000602082840312156153e057600080fd5b815180151581146112ea57600080fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121561541857600080fd5b81356112ea816150be565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60006020828403121561546457600080fd5b5051919050565b60006020828403121561547d57600080fd5b81516112ea816150be565b634e487b7160e01b600052601160045260246000fd5b600082198211156154b1576154b1615488565b500190565b6000828210156154c8576154c8615488565b500390565b60008160001904831182151516156154e7576154e7615488565b500290565b60008261550957634e487b7160e01b600052601260045260246000fd5b500490565b6000825160005b8181101561552f5760208186018101518583015201615515565b8181111561553e576000828501525b509190910192915050565b6000806040838503121561555c57600080fd5b8251615567816150be565b6020939093015192949293505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156155b6576155b6615577565b604052919050565b600067ffffffffffffffff8211156155d8576155d8615577565b5060051b60200190565b600082601f8301126155f357600080fd5b81516020615608615603836155be565b61558d565b82815260059290921b8401810191818101908684111561562757600080fd5b8286015b8481101561564b57805161563e816150be565b835291830191830161562b565b509695505050505050565b600082601f83011261566757600080fd5b81516020615677615603836155be565b82815260059290921b8401810191818101908684111561569657600080fd5b8286015b8481101561564b578051835291830191830161569a565b600080604083850312156156c457600080fd5b825167ffffffffffffffff808211156156dc57600080fd5b6156e8868387016155e2565b935060208501519150808211156156fe57600080fd5b5061570b85828601615656565b9150509250929050565b60006020828403121561572757600080fd5b815167ffffffffffffffff81111561573e57600080fd5b61109c848285016155e2565b60006020828403121561575c57600080fd5b815167ffffffffffffffff81111561577357600080fd5b61109c84828501615656565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220042140fd23aa099c914a95e26659cefe3bfa4304f57fce37f994ce9903b4919264736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb600000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d0000000000000000000000000000000000000000000000000000000000015180000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f
-----Decoded View---------------
Arg [0] : treasury (address): 0x67Df244584b67E8C51B10aD610aAfFa9a402FdB6
Arg [1] : feth (address): 0x49128CF8ABE9071ee24540a296b5DED3F9D50443
Arg [2] : royaltyRegistry (address): 0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D
Arg [3] : duration (uint256): 86400
Arg [4] : marketProxyAddress (address): 0xcDA72070E455bb31C7690a170224Ce43623d0B6f
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000067df244584b67e8c51b10ad610aaffa9a402fdb6
Arg [1] : 00000000000000000000000049128cf8abe9071ee24540a296b5ded3f9d50443
Arg [2] : 000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d
Arg [3] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [4] : 000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f
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.