{"content":{"title":"一个常规的NFT市场合约，可以用于数藏平台的搭建","body":"一个常规NFT市场合约代码，支持上架、下架NFT艺术作品，设定价格，购买NFT，配置[NFT白名单](https:\/\/learnblockchain.cn\/article\/4521)功能。\r\n\r\n```\/\/ SPDX-License-Identifier: MIT\r\npragma solidity >=0.8.0;\r\n\r\nimport \"@openzeppelin\/contracts\/utils\/math\/SafeMath.sol\";\r\nimport \"@openzeppelin\/contracts\/utils\/Address.sol\";\r\nimport \"@openzeppelin\/contracts\/token\/ERC20\/IERC20.sol\";\r\nimport \"@openzeppelin\/contracts\/token\/ERC20\/utils\/SafeERC20.sol\";\r\nimport \"@openzeppelin\/contracts\/token\/ERC721\/utils\/ERC721Holder.sol\";\r\nimport \"@openzeppelin\/contracts\/token\/ERC1155\/utils\/ERC1155Holder.sol\";\r\nimport \"@openzeppelin\/contracts\/token\/ERC721\/IERC721.sol\";\r\nimport \"@openzeppelin\/contracts\/token\/ERC1155\/IERC1155.sol\";\r\n\r\nlibrary SafeTransferLib {\r\n    \/\/ ERC721 & ERC1155 support\r\n    function safeTransferFrom (address contractAddress, address from, address to, uint256 tokenID, uint256 amount) internal returns (bool) {\r\n        try IERC721(contractAddress).safeTransferFrom(from, to, tokenID) {\r\n            return true;\r\n        } catch (bytes memory) {\r\n            try IERC1155(contractAddress).safeTransferFrom(from, to, tokenID, amount, \"0x0\") {\r\n                return true;\r\n            } catch (bytes memory) {\r\n                return false;\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\ncontract LandNFTMarket is ERC721Holder, ERC1155Holder {\r\n    using SafeMath for uint;\r\n    using SafeERC20 for IERC20;\r\n    using Address for address;\r\n\r\n    struct OnSaleNFT {\r\n        uint idx;\r\n        address nftAddress;\r\n        uint256 tokenId;\r\n        address seller;\r\n        uint256 price;\r\n        uint256 amount;\r\n    }\r\n    address public creator;\r\n    string public logoUrl; \/\/ 市场的logo图\r\n    string public bannerUrl; \/\/ 市场的banner图\r\n    address public settleToken; \/\/ 用于结算的token\r\n    mapping(address => bool) public whitelist; \/\/ 只有白名单内的NFT地址可以上架\r\n    bool public enableExternal = false;\r\n    mapping(address => mapping(uint256 => uint256)) public nftList; \/\/  nftAddress => tokenId => idx\r\n    mapping(uint256 => OnSaleNFT) public nftBriefList; \/\/ idx => tokenData\r\n    uint256 public nftCount; \/\/ idx 从1开始，只加不减\r\n    address private feeTo; \/\/ 费用接收地址\r\n\r\n    event OnSale(address indexed market, address indexed nftAddress, uint, uint);\r\n    event OffSale(address indexed market, address indexed nftAddress, uint);\r\n    event Buy(address indexed market, address indexed nftAddress, uint, uint);\r\n\r\n    constructor() {\r\n        creator = msg.sender;\r\n    }\r\n\r\n    function setSettleToken(address _settleToken) external returns(bool){\r\n        require(msg.sender == creator, 'Error: 9101');\r\n        settleToken = _settleToken;\r\n        return true;\r\n    }\r\n\r\n    \/\/ 设置费用接收地址\r\n    function setFeeTo(address _feeTo) external virtual returns(bool){\r\n        require(msg.sender == creator, 'Error: 0011');\r\n        feeTo = _feeTo;\r\n        return true;\r\n    }\r\n\r\n    \/\/ 设置市场的banner\r\n    function setLogo(string memory _logoUrl) external returns (bool) {\r\n        require(msg.sender == creator, 'Error: 9003');\r\n        logoUrl = _logoUrl;\r\n        return true;\r\n    }\r\n\r\n    \/\/ 设置市场的logo\r\n    function setBanner(string memory _bannerUrl) external returns (bool) {\r\n        require(msg.sender == creator, 'Error: 9004');\r\n        bannerUrl = _bannerUrl;\r\n        return true;\r\n    }\r\n\r\n    \/\/ 允许普通用户上架作品\r\n    function enable(bool _flag) external returns (bool) {\r\n        require(msg.sender == creator, 'Error: 9005');\r\n        enableExternal = _flag;\r\n\r\n        return true;\r\n    }\r\n\r\n    \/\/ 上架作品\r\n    function onSale(address _nftAddress, uint256 _tokenId, uint256 _price, uint256 _amount) external returns (bool) {\r\n        require(whitelist[_nftAddress] == true, 'Error: 9100');\r\n        if(msg.sender != creator){\r\n            require(enableExternal, 'Error: 9006');\r\n        }\r\n        require(nftList[_nftAddress][_tokenId] == 0, 'Error: 9007');\r\n        require(_price > 0, 'Error: 9008');\r\n        \/\/ to market\r\n        if(SafeTransferLib.safeTransferFrom(_nftAddress, msg.sender, address(this), _tokenId, _amount)){\r\n            \/\/ add to onsales\r\n            nftCount++;\r\n            nftList[_nftAddress][_tokenId] = nftCount;\r\n            OnSaleNFT memory nftBrief = OnSaleNFT(nftCount, _nftAddress, _tokenId, msg.sender, _price, _amount);\r\n            nftBriefList[nftCount] = nftBrief;\r\n\r\n            emit OnSale(address(this), _nftAddress, _tokenId, _price);\r\n\r\n            return true;\r\n        }\r\n\r\n        return false;\r\n    }\r\n\r\n    function _verify(address _nftAddress, uint256 _tokenId) internal view returns(address seller,uint256 price,uint256 amount){\r\n        ( seller, price, amount) = _getBrief(_nftAddress, _tokenId);\r\n        require(nftList[_nftAddress][_tokenId] > 0, 'Error: 9010');\r\n        require(price > 0, 'Error: 9011');\r\n        require(seller != address(0), 'Error: 9012');\r\n    }\r\n\r\n    \/\/ 下架作品\r\n    function offSale(uint idx) external returns (bool) {\r\n        OnSaleNFT memory nftBrief = nftBriefList[idx];\r\n        require(msg.sender == nftBrief.seller || msg.sender == creator, 'Error: 9014');\r\n        require(nftBrief.nftAddress != address(0), 'Error: 9015');\r\n        require(nftBrief.tokenId > 0, 'Error: 9016');\r\n        require(nftBrief.amount > 0, 'Error: 9017');\r\n        \/\/ to seller\r\n        if(SafeTransferLib.safeTransferFrom(nftBrief.nftAddress, address(this), nftBrief.seller, nftBrief.tokenId, nftBrief.amount)){\r\n            delete nftBriefList[idx];\r\n            delete nftList[nftBrief.nftAddress][nftBrief.tokenId];\r\n\r\n            emit OffSale(address(this), nftBrief.nftAddress, nftBrief.tokenId);\r\n\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    function _getBrief(address _nftAddress, uint256 _tokenId) private view returns(address seller, uint256 price, uint256 amount){\r\n        uint256 idx = nftList[_nftAddress][_tokenId];\r\n        OnSaleNFT memory nftBrief = nftBriefList[idx];\r\n        (seller, price, amount) = (nftBrief.seller, nftBrief.price, nftBrief.amount);\r\n    }\r\n\r\n    function getBrief(address _nftAddress, uint256 _tokenId) external view returns(address seller, uint256 price, uint256 amount){\r\n        return _getBrief(_nftAddress, _tokenId);\r\n    }\r\n\r\n    \/\/ 购买作品\r\n    function buy(uint idx, uint256 _amount) external returns (bool) {\r\n        OnSaleNFT memory nftBrief = nftBriefList[idx];\r\n        \/\/ check amount\r\n        require(nftBrief.amount >= _amount, 'Error: 9015');\r\n        require(nftBrief.price > 0, 'Error: 9016');\r\n        require(nftBrief.seller != address(0), 'Error: 9012');\r\n        require(nftBrief.nftAddress != address(0), 'Error: 9015');\r\n        require(nftBrief.tokenId > 0, 'Error: 9016');\r\n        uint256 totalPrice = uint(nftBrief.price).mul(_amount);\r\n        \/\/ to seller\r\n        IERC20(settleToken).safeTransferFrom(msg.sender, nftBrief.seller, totalPrice);\r\n        \/\/ to buyer\r\n        if(SafeTransferLib.safeTransferFrom(nftBrief.nftAddress, address(this), msg.sender, nftBrief.tokenId, 1)){\r\n            \/\/ remove from onsales\r\n            \/\/ NFT\r\n            delete nftBriefList[idx];\r\n            delete nftList[nftBrief.nftAddress][nftBrief.tokenId];\r\n\r\n            emit Buy(address(this), nftBrief.nftAddress, nftBrief.tokenId, totalPrice);\r\n\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    \/\/ 分页获取在售作品，每页10个\r\n    function getNftList(uint startIdx) external view returns (OnSaleNFT[] memory) {\r\n        uint256 counter = 0;\r\n        for(uint256 i = 1; i <= nftCount; i++){\r\n            if(nftBriefList[i].tokenId > 0){\r\n                counter++;\r\n            }\r\n        }\r\n        OnSaleNFT[] memory _lists = new OnSaleNFT[](counter);\r\n        if (counter != 0) {\r\n            uint256 itemCounter = 0;\r\n            for(uint256 i = startIdx + 1; i <= nftCount; i++){\r\n                if(nftBriefList[i].tokenId > 0){\r\n                    _lists[itemCounter] = nftBriefList[i];\r\n                    itemCounter++;\r\n                    if(itemCounter >= 10){\r\n                        break;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return _lists;\r\n    }\r\n\r\n    \/\/ 获取指定用户出售中的作品\r\n    function getNftListBySeller(address seller) external view returns (OnSaleNFT[] memory) {\r\n        uint256 counter = 0;\r\n        for(uint256 i = 1; i <= nftCount; i++){\r\n            if(nftBriefList[i].seller == seller){\r\n                counter++;\r\n            }\r\n        }\r\n        OnSaleNFT[] memory _lists = new OnSaleNFT[](counter);\r\n        if (counter != 0) {\r\n            uint256 itemCounter = 0;\r\n            for(uint256 i = 1; i <= nftCount; i++){\r\n                if(nftBriefList[i].seller == seller){\r\n                    _lists[itemCounter] = nftBriefList[i];\r\n                    itemCounter++;\r\n                }\r\n            }\r\n        }\r\n        return _lists;\r\n    }\r\n\r\n    function setNFTWhitelist(address _address, bool _status) external returns(bool){\r\n        require(msg.sender == creator, 'Error: 1009');\r\n        whitelist[_address] = _status;\r\n        return true;\r\n    }\r\n}\r\n```"},"author":{"user":"https:\/\/learnblockchain.cn\/people\/10028","address":null},"history":"QmQcnKvznFZjBhSvX5jpQjeWUkE1NYuNgbgPSJeV6d86jb","timestamp":1662453689,"version":1}