// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract BENFTMarket is Ownable, ReentrancyGuard, ERC1155Holder,ERC721Holder { using SafeERC20 for IERC20; struct OrderInfo { uint256 orderId; uint256 tokenId; uint256 amount; address owner; uint256 price; address nftToken; address currency; } mapping(address => bool) public erc721Supported; mapping(address => bool) public erc1155Supported; mapping(address => bool) public erc20Supported; mapping(uint256 => OrderInfo) public orderInfos; mapping(address => uint256) public nftPriceMaxLimit; mapping(address => uint256) public nftPriceMinLimit; event SellOrder( uint256 indexed tokenId, address indexed owner, address indexed nftToken, uint256 amount, uint256 orderId, address currency, uint256 price ); event CancelOrder( uint256 indexed orderId, address indexed nftToken, uint256 indexed tokenId ); event PriceUpdate( uint256 indexed orderId, address indexed nftToken, uint256 indexed tokenId, uint256 priceOld, uint256 price ); event BuyOrder( uint256 indexed tokenId, uint256 orderId, address nftToken, uint256 amount, address seller, address buyer, address erc20, uint256 price ); event AddNFTSuppout(address nftToken); event RemoveNFTSuppout(address nftToken); event AddERC20Suppout(address erc20); event RemoveERC20Suppout(address erc20); uint256 public tranFeeTotal; uint256 constant ROUND = 1000000; uint256 public transactionFee = (3 * ROUND) / 100; // min transaction fee is: 0.5% uint256 public constant MIN_TRANSACTION_FEE = (5 * ROUND) / 1000; // max transaction fee is: 10% uint256 public constant MAX_TRANSACTION_FEE = (10 * ROUND) / 100; address public feeToAddress; uint256 public incrId; function sell( address nftToken, address currency, uint256 tokenId, uint256 price, uint256 amount ) external { require(tokenId != 0, "NFTMarket: tokenId can not be 0!"); require( erc721Supported[nftToken] || erc1155Supported[nftToken], "NFTMarket: Unsupported NFT" ); require(erc20Supported[currency], "NFTMarket: Unsupported tokens"); require( price <= nftPriceMaxLimit[nftToken] || nftPriceMaxLimit[nftToken] == 0, "NFTMarket: Maximum price limit exceeded" ); require( price >= nftPriceMinLimit[nftToken] && price != 0, "NFTMarket: Below the minimum price limit" ); incrId += 1; OrderInfo storage orderInfo = orderInfos[incrId]; orderInfo.orderId = incrId; orderInfo.tokenId = tokenId; orderInfo.amount = amount; orderInfo.nftToken = nftToken; orderInfo.owner = msg.sender; orderInfo.price = price; orderInfo.currency = currency; if (erc721Supported[nftToken]) { require(amount == 1, "NFTMarket: ERC721 amount must be 1 "); IERC721(nftToken).safeTransferFrom(msg.sender, address(this), tokenId); } else if (erc1155Supported[nftToken]) { IERC1155(nftToken).safeTransferFrom( msg.sender, address(this), tokenId, amount, "" ); } emit SellOrder( tokenId, msg.sender, nftToken, amount, incrId, currency, price ); } function buy(uint256 orderId) external nonReentrant { OrderInfo memory orderInfo = orderInfos[orderId]; require(orderInfo.tokenId != 0, "NFTMarket: NFT does not exist"); uint256 _transactionFee = (orderInfo.price * transactionFee) / ROUND; tranFeeTotal = tranFeeTotal + _transactionFee; uint256 _amount = orderInfo.price - _transactionFee; IERC20(orderInfo.currency).safeTransferFrom( msg.sender, orderInfo.owner, _amount ); IERC20(orderInfo.currency).safeTransferFrom( msg.sender, feeToAddress, _transactionFee ); if (erc721Supported[orderInfo.nftToken]) { IERC721(orderInfo.nftToken).safeTransferFrom( address(this), msg.sender, orderInfo.tokenId ); } else if (erc1155Supported[orderInfo.nftToken]) { IERC1155(orderInfo.nftToken).safeTransferFrom( address(this), msg.sender, orderInfo.tokenId, orderInfo.amount, "" ); } emit BuyOrder( orderInfo.tokenId, orderId, orderInfo.nftToken, orderInfo.amount, orderInfo.owner, msg.sender, orderInfo.currency, orderInfo.price ); delete orderInfos[orderId]; } function cancelOrder(uint256 orderId) external nonReentrant { require( orderInfos[orderId].owner == msg.sender, "NFTMarket: cancel caller is not owner" ); OrderInfo memory orderInfo = orderInfos[orderId]; if (erc721Supported[orderInfo.nftToken]) { IERC721(orderInfo.nftToken).safeTransferFrom( address(this), msg.sender, orderInfo.tokenId ); } else if (erc1155Supported[orderInfo.nftToken]) { IERC1155(orderInfo.nftToken).safeTransferFrom( address(this), msg.sender, orderInfo.tokenId, orderInfo.amount, "" ); } delete orderInfos[orderId]; emit CancelOrder(orderId, orderInfo.nftToken, orderInfo.tokenId); } function updatePrice(uint256 orderId, uint256 price) external { OrderInfo memory orderInfo = orderInfos[orderId]; require(orderInfo.tokenId != 0, "NFTMarket: NFT does not exist"); require(orderInfo.owner == msg.sender, "NFTMarket: caller is not owner"); require( price <= nftPriceMaxLimit[orderInfo.nftToken] || nftPriceMaxLimit[orderInfo.nftToken] == 0, "NFTMarket: Maximum price limit exceeded" ); require( price >= nftPriceMinLimit[orderInfo.nftToken] && price != 0, "NFTMarket: Below the minimum price limit" ); uint256 priceOld = orderInfo.price; orderInfo.price = price; emit PriceUpdate(orderId, orderInfo.nftToken, orderInfo.tokenId, priceOld, price); } function addERC721Support(address nftToken) external onlyOwner { erc721Supported[nftToken] = true; emit AddNFTSuppout(nftToken); } function removeERC721Support(address nftToken) external onlyOwner { erc721Supported[nftToken] = false; emit RemoveNFTSuppout(nftToken); } function addERC1155Support(address nftToken) external onlyOwner { erc1155Supported[nftToken] = true; emit AddNFTSuppout(nftToken); } function removeERC1155Support(address nftToken) external onlyOwner { erc1155Supported[nftToken] = false; emit RemoveNFTSuppout(nftToken); } function addERC20Support(address erc20) external onlyOwner { require(erc20 != address(0), "NFTMarket: ERC20 address is zero"); erc20Supported[erc20] = true; emit AddERC20Suppout(erc20); } function removeERC20Support(address erc20) external onlyOwner { erc20Supported[erc20] = false; emit RemoveERC20Suppout(erc20); } function setNFTPriceMaxLimit(address nftToken, uint256 maxLimit) external onlyOwner { require( maxLimit >= nftPriceMinLimit[nftToken], "NFTMarket: maxLimit can not be less than min limit!" ); nftPriceMaxLimit[nftToken] = maxLimit; } function setNFTPriceMinLimit(address nftToken, uint256 minLimit) external onlyOwner { if (nftPriceMaxLimit[nftToken] != 0) { require( minLimit <= nftPriceMaxLimit[nftToken], "NFTMarket: minLimit can not be larger than max limit!" ); } nftPriceMinLimit[nftToken] = minLimit; } function setTransactionFee(uint256 _transactionFee) external onlyOwner { require( _transactionFee >= MIN_TRANSACTION_FEE && _transactionFee <= MAX_TRANSACTION_FEE, "NFTMarket: _transactionFee must >= 0.5% and <= 10%" ); transactionFee = _transactionFee; } function setFeeToAddress(address _feeToAddress) external onlyOwner { require(_feeToAddress != address(0), "NFTMarket: fee received address can not be zero"); feeToAddress = _feeToAddress; } }