300 lines
8.4 KiB
Solidity
300 lines
8.4 KiB
Solidity
// 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;
|
|
}
|
|
}
|