NFT Marketplace
NFT Marketplace
本页内容正在整理中,欢迎贡献。
概述
NFT Marketplace 允许用户上架、购买和竞拍 NFT。本文介绍如何实现一个简单但功能完整的 NFT 交易市场,涵盖固定价格出售、版税分配和托管模式。
主要内容
Marketplace 合约架构
NFT Marketplace├── 上架(List) 卖家授权 Marketplace 并设置价格├── 取消上架 卖家取消销售├── 购买(Buy) 买家支付 ETH,Marketplace 转移 NFT├── 竞拍(Bid) 竞价模式(可选)└── 版税分配 从销售额中扣除版税给创作者简单固定价格 Marketplace
// SPDX-License-Identifier: MITpragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";import "@openzeppelin/contracts/access/Ownable.sol";import "@openzeppelin/contracts/interfaces/IERC2981.sol";
contract NFTMarketplace is ReentrancyGuard, Ownable {
struct Listing { address seller; uint256 price; bool active; }
// nftContract => tokenId => Listing mapping(address => mapping(uint256 => Listing)) public listings;
uint256 public platformFeeBps = 250; // 2.5%(基于 10000) uint256 private _platformFees;
event Listed(address indexed nft, uint256 indexed tokenId, address seller, uint256 price); event Sold(address indexed nft, uint256 indexed tokenId, address buyer, uint256 price); event Cancelled(address indexed nft, uint256 indexed tokenId);
constructor() Ownable(msg.sender) {}
// 上架 NFT function listNFT( address nftContract, uint256 tokenId, uint256 price ) external { require(price > 0, "Price must be > 0"); IERC721 nft = IERC721(nftContract); require(nft.ownerOf(tokenId) == msg.sender, "Not owner"); require( nft.isApprovedForAll(msg.sender, address(this)) || nft.getApproved(tokenId) == address(this), "Marketplace not approved" );
listings[nftContract][tokenId] = Listing({ seller: msg.sender, price: price, active: true });
emit Listed(nftContract, tokenId, msg.sender, price); }
// 购买 NFT function buyNFT( address nftContract, uint256 tokenId ) external payable nonReentrant { Listing storage listing = listings[nftContract][tokenId]; require(listing.active, "Not listed"); require(msg.value >= listing.price, "Insufficient payment");
listing.active = false; uint256 salePrice = listing.price; address seller = listing.seller;
// 计算平台费 uint256 platformFee = (salePrice * platformFeeBps) / 10000; uint256 royaltyAmount = 0;
// 计算版税(ERC-2981) if (IERC165(nftContract).supportsInterface(type(IERC2981).interfaceId)) { (address royaltyReceiver, uint256 royalty) = IERC2981(nftContract) .royaltyInfo(tokenId, salePrice); if (royaltyReceiver != address(0) && royaltyReceiver != seller) { royaltyAmount = royalty; payable(royaltyReceiver).transfer(royaltyAmount); } }
// 转账给卖家 uint256 sellerProceeds = salePrice - platformFee - royaltyAmount; _platformFees += platformFee; payable(seller).transfer(sellerProceeds);
// 转移 NFT IERC721(nftContract).safeTransferFrom(seller, msg.sender, tokenId);
// 退还多余 ETH if (msg.value > salePrice) { payable(msg.sender).transfer(msg.value - salePrice); }
emit Sold(nftContract, tokenId, msg.sender, salePrice); }
// 取消上架 function cancelListing(address nftContract, uint256 tokenId) external { Listing storage listing = listings[nftContract][tokenId]; require(listing.active, "Not listed"); require(listing.seller == msg.sender, "Not seller");
listing.active = false; emit Cancelled(nftContract, tokenId); }
// 修改价格 function updatePrice( address nftContract, uint256 tokenId, uint256 newPrice ) external { Listing storage listing = listings[nftContract][tokenId]; require(listing.active, "Not listed"); require(listing.seller == msg.sender, "Not seller"); require(newPrice > 0, "Price must be > 0");
listing.price = newPrice; }
// 提取平台费 function withdrawFees() external onlyOwner { uint256 amount = _platformFees; _platformFees = 0; payable(owner()).transfer(amount); }}前端集成
import { useWriteContract, useReadContract } from "wagmi";import { parseEther, formatEther } from "viem";
// 上架 NFTfunction ListNFTButton({ nftAddress, tokenId, priceEth }: { nftAddress: `0x${string}`, tokenId: bigint, priceEth: string}) { const { writeContract: approveNFT } = useWriteContract(); const { writeContract: listNFT } = useWriteContract();
const handleList = async () => { // Step 1: 授权 Marketplace await approveNFT({ address: nftAddress, abi: ERC721_ABI, functionName: "setApprovalForAll", args: [MARKETPLACE_ADDRESS, true], });
// Step 2: 上架 await listNFT({ address: MARKETPLACE_ADDRESS, abi: MARKETPLACE_ABI, functionName: "listNFT", args: [nftAddress, tokenId, parseEther(priceEth)], }); };
return <button onClick={handleList}>上架 NFT</button>;}主流 NFT 市场参考
| 平台 | 特色 | 版税支持 |
|---|---|---|
| OpenSea | 最大综合市场 | 可选(争议) |
| Blur | 专业交易者,零版税选项 | 可选 |
| Magic Eden | 多链支持 | 支持 |
| Reservoir | 聚合 API | 取决于来源 |