// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol



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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: contracts/NftMarketV2.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

//    ____                      _  __    _   ______________   __  ___           __        __ 
//   / __ \____  ___  ____     | |/ /   / | / / ____/_  __/  /  |/  /___ ______/ /_____  / /_
//  / / / / __ \/ _ \/ __ \    |   /   /  |/ / /_    / /    / /|_/ / __ `/ ___/ //_/ _ \/ __/
// / /_/ / /_/ /  __/ / / /   /   |   / /|  / __/   / /    / /  / / /_/ / /  / ,< /  __/ /_  
// \____/ .___/\___/_/ /_/   /_/|_|  /_/ |_/_/     /_/    /_/  /_/\__,_/_/  /_/|_|\___/\__/  
//     /_/                                                                                   
// © The OpenX Project. Made with love by Alex. Keep the dream alive.


contract NftMarketv2 is Ownable {
	
	//fee per 10k
	uint256 public feePer10K = 100;
	//WETH
	address public WETH;

	struct nftlisting {
		address nft;
		address seller;
		address buyer;
		uint256 tokenId;
		address fundingToken;
		address[] whitelist;
		uint256 price;
		uint256 startDate;
		uint256 endDate;
		uint256 saleDate;
	}

	struct bid {
		uint256 listingID;
		address fundingToken;
		uint256 price;
		address user;
	}

	struct offer {
		address nft;
		uint256 tokenId;
		address fundingToken;
		uint256 price;
		address user;
	}

	struct collection{
		address nft;
		address fundingToken;
		uint256 price;
		address seller;
		address[] whitelist;	
	}

	uint internal _unlocked = 1;
    modifier lock() {
        require(_unlocked == 1);
        _unlocked = 2;
        _;
        _unlocked = 1;
    }

	nftlisting[] public Listings;
	collection[] public Collections;
	bid[] public Bids;
	offer[] public Offers;

	mapping(address=> uint256) public nftRoyaltyFee;
	mapping(address=> address) public nftRoyaltyFeeTo;
	mapping(address=> string) public nftContractUrl;

	event nftListed(address indexed seller,	address indexed nft, uint256 tokenId, address fundingToken,
	 				uint256 price, address[] whitelist, uint256 startDate, uint256 endDate, uint256 listingID);
	event Bid(uint256 indexed bidId,uint256 indexed listingID,address fundingToken,uint256 price,address user);
	event Offer(uint256 offerId, address nft, uint256 tokenId, address fundingToken, uint256 price, address owner);
	event nftSold(uint256 listingID);
	event AcceptBid(uint256 indexed bidId);
	event AcceptOffer(uint256 indexed bidId);
	event removeNft(uint256 indexed listingID);
	event updateRoyalties(address indexed nft,address to,uint256 _feePer10K);
	event updateContractMetadata(address indexed nft, string metadata);
	event updateMarketFee(uint256 amount);
	event msgEvent(address indexed from, address indexed to, uint256 id, string msg);
	event CollectionRemoved(uint256 collectionId);
	event CollectionListed(uint256 collectionId, address nft, address fundingToken, uint256 price, address[] whitelist, address seller);
	event CollectionSold(uint256 collectionId, uint256 tokenId);

	constructor(address _WETH){
		WETH = _WETH;
		require(msg.sender == 0xFC8367eF6bcfC10746e5B9B034314812db8D7213);
		emit updateMarketFee(100);
	}

	function getListingLength() public view returns(uint256){
		return Listings.length;
	}

	function getWhitelistLength(uint256 _listingID) public view returns(uint256){
		return Listings[_listingID].whitelist.length;
	}

	function removeListing(uint256 _listingID) public {
		if(msg.sender != owner()){
			require(msg.sender == Listings[_listingID].seller);
		}
		require(Listings[_listingID].saleDate == 0, "Already sold.");
		require(Listings[_listingID].endDate > block.timestamp, "End date past.");
		Listings[_listingID].saleDate = 420;
		delete Listings[_listingID];
		emit removeNft(_listingID);
	}

	function listNFT(address _nft, uint256 _tokenId, address _fundingToken, uint256 _price, address[] memory _whitelist, uint256 _startDate, uint256 _endDate) public {
		require(IERC721(_nft).ownerOf(_tokenId) == msg.sender, "Cant sell what you dont own.");
		require(IERC721(_nft).getApproved(_tokenId) == address(this), "Must Be Approved.");
		nftlisting memory newListing;
		newListing.nft = _nft;	
		newListing.seller = msg.sender;
		newListing.tokenId = _tokenId;
		newListing.fundingToken = _fundingToken;
		newListing.price = _price;
		newListing.whitelist = _whitelist;
		newListing.startDate = _startDate;
		newListing.endDate = _endDate;

		uint256 id = Listings.length;
		Listings.push(newListing);
		emit nftListed(msg.sender, _nft,_tokenId, _fundingToken, _price, _whitelist, _startDate, _endDate, id);
	}

	function listNFTs(address _nft, uint256[] memory _tokenIds, address[] memory _fundingTokens, uint256[] memory _prices, address[] memory _whitelist, uint256 _startDate, uint256 _endDate) public {
		require(_tokenIds.length == _prices.length, "invalid Array");
		for(uint256 id; id > _tokenIds.length; id++){
			uint256 _tokenId = _tokenIds[id];
			uint256 _price = _prices[id];
			address _fundingToken = _fundingTokens[id];
			require(IERC721(_nft).ownerOf(_tokenId) == msg.sender, "Cant sell what you dont own.");
			require(IERC721(_nft).getApproved(_tokenId) == address(this), "Must Be Approved.");
			nftlisting memory newListing;
			newListing.nft = _nft;	
			newListing.seller = msg.sender;
			newListing.tokenId = _tokenId;
			newListing.fundingToken = _fundingToken;
			newListing.price = _price;
			newListing.whitelist = _whitelist;
			newListing.startDate = _startDate;
			newListing.endDate = _endDate;

			uint256 listingId = Listings.length;
			Listings.push(newListing);
			emit nftListed(msg.sender, _nft,_tokenId, _fundingToken, _price, _whitelist, _startDate, _endDate, listingId);
		}
		
	}

	function listCollection(address _nft, address _fundingToken, uint256 _price, address[] memory _whitelist) public {
		require(Ownable(_nft).owner() == msg.sender, "Must Be Owner");
		require(IERC721(_nft).isApprovedForAll(msg.sender, address(this)), "Not approved for all.");
		collection memory newCollection;
		newCollection.nft = _nft;
		newCollection.fundingToken = _fundingToken;
		newCollection.price = _price;
		newCollection.whitelist = _whitelist;
		newCollection.seller = msg.sender;
		uint256 colId = Collections.length;
		Collections.push(newCollection);

		emit CollectionListed(colId, _nft, _fundingToken, _price, _whitelist, msg.sender);
	}

	function removeCollection(uint256 _collectionId) public {
		if(msg.sender != owner()){
			require(Ownable(Collections[_collectionId].nft).owner() == msg.sender, "Must Be Owner");
			require(!IERC721(Collections[_collectionId].nft).isApprovedForAll(msg.sender, address(this)), "Approved for all");
		}

		delete Collections[_collectionId];
		emit CollectionRemoved(_collectionId);
	}

	function buyCollection(uint256 _collectionId, uint256 _tokenId) public payable lock {
		uint256 feeAmount = Collections[_collectionId].price * feePer10K / 10000;
		uint256 feeAmountRoyalty = Collections[_collectionId].price * nftRoyaltyFee[Collections[_collectionId].nft] / 10000;

		if(Collections[_collectionId].whitelist.length > 0){
			require(_checkWhitelist(Collections[_collectionId].whitelist, msg.sender), "Private sale.");
		}

		IERC721(Collections[_collectionId].nft).safeTransferFrom(Collections[_collectionId].seller, msg.sender, _tokenId);

		bool success;
		if(Collections[_collectionId].fundingToken == WETH){
			require(Collections[_collectionId].price == msg.value, "Missing ETH");

			if(feeAmountRoyalty>0){
				safeTransferETH(nftRoyaltyFeeTo[Collections[_collectionId].nft], feeAmountRoyalty);
			}

			safeTransferETH(owner(), feeAmount);
			safeTransferETH(Collections[_collectionId].seller, Collections[_collectionId].price - feeAmount - feeAmountRoyalty);

		}else{
			if(feeAmountRoyalty>0){
				success = IERC20(Collections[_collectionId].fundingToken).transferFrom(msg.sender, nftRoyaltyFeeTo[Collections[_collectionId].nft], feeAmountRoyalty);
				require(success, "Transfer From Failed.");
			}
			success = IERC20(Collections[_collectionId].fundingToken).transferFrom(msg.sender, Collections[_collectionId].seller, Collections[_collectionId].price - feeAmount - feeAmountRoyalty);
			require(success, "Transfer From Failed.");
			success = IERC20(Collections[_collectionId].fundingToken).transferFrom(msg.sender, owner(), feeAmount);
			require(success, "Transfer From Failed.");
		}
		emit CollectionSold(_collectionId, _tokenId);
	}

	function buyNFT(uint256 _listingID) public payable lock {
		require(Listings[_listingID].startDate < block.timestamp, "Start date not past.");
		require(Listings[_listingID].endDate > block.timestamp, "End date past.");
		require(Listings[_listingID].saleDate == 0, "Already sold.");
		uint256 feeAmount = Listings[_listingID].price * feePer10K / 10000;
		uint256 feeAmountRoyalty = Listings[_listingID].price * nftRoyaltyFee[Listings[_listingID].nft] / 10000;

		if(Listings[_listingID].whitelist.length > 0){
			require(_checkWhitelist(Listings[_listingID].whitelist, msg.sender), "Private sale.");
		}

		IERC721(Listings[_listingID].nft).safeTransferFrom(Listings[_listingID].seller, msg.sender, Listings[_listingID].tokenId);
		Listings[_listingID].saleDate = block.timestamp;

		bool success;
		if(Listings[_listingID].fundingToken == WETH){
			require(Listings[_listingID].price == msg.value, "Missing ETH");

			if(feeAmountRoyalty>0){
				safeTransferETH(nftRoyaltyFeeTo[Listings[_listingID].nft], feeAmountRoyalty);
			}

			safeTransferETH(owner(), feeAmount);
			safeTransferETH(Listings[_listingID].seller, Listings[_listingID].price - feeAmount - feeAmountRoyalty);

		}else{
			if(feeAmountRoyalty>0){
				success = IERC20(Listings[_listingID].fundingToken).transferFrom(msg.sender, nftRoyaltyFeeTo[Listings[_listingID].nft], feeAmountRoyalty);
				require(success, "Transfer From Failed.");
			}
			success = IERC20(Listings[_listingID].fundingToken).transferFrom(msg.sender, Listings[_listingID].seller, Listings[_listingID].price - feeAmount - feeAmountRoyalty);
			require(success, "Transfer From Failed.");
			success = IERC20(Listings[_listingID].fundingToken).transferFrom(msg.sender, owner(), feeAmount);
			require(success, "Transfer From Failed.");
		}
		emit nftSold(_listingID);
	}


	function bidListing(uint256 _listingID,address _fundingToken, uint256 _price) public {
		bid memory newBid;
		newBid.listingID = _listingID;
		newBid.fundingToken = _fundingToken;
		newBid.price = _price;
		newBid.user = msg.sender;
		uint256 _bidID = Bids.length;
		Bids.push(newBid);
		emit Bid(_bidID, _listingID, _fundingToken, _price, msg.sender);
	}

	function makeOffer(address _nft, uint256 _tokenId, address _fundingToken, uint256 _price) public {
		address owner = IERC721(_nft).ownerOf(_tokenId);
		offer memory newOffer;
		newOffer.nft = _nft;
		newOffer.tokenId = _tokenId;
		newOffer.fundingToken = _fundingToken;
		newOffer.price = _price;
		newOffer.user = msg.sender;
		uint256 offerId = Offers.length;
		Offers.push(newOffer);
		emit Offer(offerId, _nft, _tokenId, _fundingToken, _price, owner);
	}

	function acceptOffer(uint256 _offerId) public {
		offer storage currentOffer = Offers[_offerId];
		require(IERC721(currentOffer.nft).ownerOf(currentOffer.tokenId) == msg.sender, "Cant sell what you dont own.");
		require(IERC721(currentOffer.nft).getApproved(currentOffer.tokenId) == address(this), "Must Be Approved.");

		address[] memory whitelist = new address[](1);
		whitelist[0] = currentOffer.user;
		nftlisting memory newListing;

		newListing.nft = currentOffer.nft;	
		newListing.seller = msg.sender;
		newListing.tokenId = currentOffer.tokenId;
		newListing.fundingToken = currentOffer.fundingToken;
		newListing.price = currentOffer.price;
		newListing.whitelist = whitelist;
		newListing.startDate = block.timestamp;
		newListing.endDate = block.timestamp + (60*60*24*7*4);

		uint256 id = Listings.length;
		Listings.push(newListing);
		emit nftListed(msg.sender, newListing.nft,newListing.tokenId, newListing.fundingToken, newListing.price, newListing.whitelist, newListing.startDate, newListing.endDate, id);
		emit AcceptOffer(_offerId);
	}

	function setRoyalties(address _nft,address _to, uint256 _feePer10K) public {
		if(msg.sender != owner()){
			require(Ownable(_nft).owner() == msg.sender, "Not owner");
		}
		require(_feePer10K <= 2500, "Unsupported fee tier.");

		nftRoyaltyFee[_nft] = _feePer10K;
		nftRoyaltyFeeTo[_nft] = _to;

		emit updateRoyalties(_nft, _to, _feePer10K);
	}

	function updateContractMetadataUrl(address _nft,string memory _metadataUrl) public {
		if(msg.sender != owner()){
			require(Ownable(_nft).owner() == msg.sender, "Not owner");
		}
		nftContractUrl[_nft] = _metadataUrl;
		emit updateContractMetadata(_nft, _metadataUrl);
	}

	function acceptBid(uint256 _bidID) public {
		bid storage currentBid = Bids[_bidID];
		uint256 _listingID = currentBid.listingID;

		require(Listings[_listingID].seller == msg.sender, "Not lister");

		Listings[_listingID].whitelist = [currentBid.user];
		Listings[_listingID].fundingToken = currentBid.fundingToken;
		Listings[_listingID].price = currentBid.price;
		emit AcceptBid(_bidID);
	}

	function _checkWhitelist(address[] memory _whitelist, address _user) internal pure returns(bool){
		for(uint256 i;i<_whitelist.length;i++){
			if(_whitelist[i] == _user){
				return true;
			}
		}
		return false;
	}

	function message(address _to, uint256 _id, string memory _msg) public {
		emit msgEvent(msg.sender,_to,_id,_msg);
	}

	function collectFees(address _token) public onlyOwner{
		uint256 bal = IERC20(_token).balanceOf(address(this));
		bool succ = IERC20(_token).transfer(owner(), bal);
		require(succ, "Transfer Failed.");
	}

	function setFee(uint256 _feePer10K) public onlyOwner {
		require(_feePer10K <= 500, "Unsupported fee tier.");
		feePer10K = _feePer10K;
		emit updateMarketFee(_feePer10K);
	}

	function safeTransferETH(address to, uint _value) internal {
        (bool success,) = to.call{value:_value}(new bytes(0));
        require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
    }

}