实现一个简洁版的NFT交易所

前言

本文实现一个简单版的NFT交易所,主要包含上架、下架、更新价格、购买NFT等相关功能

NFT合约说明

关于NFT合约的开发、测试、部署具体实现,可以查看另一篇:快速实现一个标准的NFT合约(实操篇)

NFT交易所合约

合约说明具备上架、下架、更新价格、购买NFT

java 复制代码
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.22;
import "hardhat/console.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "./NFT.sol";//实现的nft的合约

contract NFTSwap is IERC721Receiver {
    event List(
        address indexed seller,
        address indexed nftAddr,
        uint256 indexed tokenId,
        uint256 price
    );
    event Purchase(
        address indexed buyer,
        address indexed nftAddr,
        uint256 indexed tokenId,
        uint256 price
    );
    event Revoke(
        address indexed seller,
        address indexed nftAddr,
        uint256 indexed tokenId
    );
    event Update(
        address indexed seller,
        address indexed nftAddr,
        uint256 indexed tokenId,
        uint256 newPrice
    );
    receive() external payable {}//解决没有接受函数的问题
    // 定义order结构体
    struct Order {
        address owner;
        uint256 price;
    }
    // NFT Order映射
    mapping(address => mapping(uint256 => Order)) public nftList;

    fallback() external payable {}
    
    // 挂单: 卖家上架NFT,合约地址为_nftAddr,tokenId为_tokenId,价格_price为以太坊(单位是wei)
    function list(address _nftAddr, uint256 _tokenId, uint256 _price) public {
        IERC721 _nft = IERC721(_nftAddr); // 声明IERC721接口合约变量
        require(_nft.getApproved(_tokenId) == address(this), "Need Approval"); // 合约得到授权
        require(_price > 0); // 价格大于0

        Order storage _order = nftList[_nftAddr][_tokenId]; //设置NFT持有人和价格
        _order.owner = msg.sender;
        _order.price = _price;
        // 将NFT转账到合约
        _nft.safeTransferFrom(msg.sender, address(this), _tokenId);

        // 释放List事件
        emit List(msg.sender, _nftAddr, _tokenId, _price);
    }

    // 购买: 买家购买NFT,合约为_nftAddr,tokenId为_tokenId,调用函数时要附带ETH
    function purchase(address _nftAddr, uint256 _tokenId) public payable {
        Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
        require(_order.price > 0, "Invalid Price"); // NFT价格大于0
        require(msg.value >= _order.price, "Increase price"); // 购买价格大于标价
        // 声明IERC721接口合约变量
        IERC721 _nft = IERC721(_nftAddr);
        require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中

        // 将NFT转给买家
        _nft.safeTransferFrom(address(this), msg.sender, _tokenId);
        // 将ETH转给卖家
        payable(_order.owner).transfer(_order.price);
        // 多余ETH给买家退款
        if (msg.value > _order.price) {
            payable(msg.sender).transfer(msg.value - _order.price);
        }

        // 释放Purchase事件
        emit Purchase(msg.sender, _nftAddr, _tokenId, _order.price);

        delete nftList[_nftAddr][_tokenId]; // 删除order
    }

    // 撤单: 卖家取消挂单
    function revoke(address _nftAddr, uint256 _tokenId) public {
        Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
        require(_order.owner == msg.sender, "Not Owner"); // 必须由持有人发起
        // 声明IERC721接口合约变量
        IERC721 _nft = IERC721(_nftAddr);
        require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中

        // 将NFT转给卖家
        _nft.safeTransferFrom(address(this), msg.sender, _tokenId);
        delete nftList[_nftAddr][_tokenId]; // 删除order

        // 释放Revoke事件
        emit Revoke(msg.sender, _nftAddr, _tokenId);
    }

    // 调整价格: 卖家调整挂单价格
    function update(
        address _nftAddr,
        uint256 _tokenId,
        uint256 _newPrice
    ) public {
        require(_newPrice > 0, "Invalid Price"); // NFT价格大于0
        Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
        require(_order.owner == msg.sender, "Not Owner"); // 必须由持有人发起
        // 声明IERC721接口合约变量
        IERC721 _nft = IERC721(_nftAddr);
        require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中

        // 调整NFT价格
        _order.price = _newPrice;

        // 释放Update事件
        emit Update(msg.sender, _nftAddr, _tokenId, _newPrice);
    }

    // 实现{IERC721Receiver}的onERC721Received,能够接收ERC721代币
    function onERC721Received(
        address operator,
        address from,
        uint tokenId,
        bytes calldata data
    ) external override returns (bytes4) {
        return IERC721Receiver.onERC721Received.selector;
    }
}

NFT交易所测试

javascript 复制代码
const {ethers,getNamedAccounts,deployments} = require("hardhat");
const { assert,expect } = require("chai");
describe("NFTSwap",function(){
    let nft;//合约
    let nftswap;//合约
    let addr1;
    let addr2;
    let firstAccount//第一个账户
    let secondAccount//第二个账户
    let mekadata='ipfs://QmQT8VpmWQVhUhoDCEK1mdHXaFaJ3KawkRxHm96GUhrXLB';//mekadata关于说明nft相关描述,属性的json文件
    let mekadata1="ipfs://QmXzbsbjpWpbSGJkgGzmk6r6HLz1nvjpEtjFR6bVhMh3U9";//同上
    beforeEach(async function(){
        await deployments.fixture(["nft","nftswap"]);//部署nft和nft交易所
        [addr1,addr2]=await ethers.getSigners();//获取账号(说明:这样获取的账号支持签名操作,例如:如果使用secondAccount就会报错VoidSigner cannot sign transactions)
        firstAccount=(await getNamedAccounts()).firstAccount;
        secondAccount=(await getNamedAccounts()).secondAccount;
        const nftDeployment = await deployments.get("BoykaNFT");
        nft = await ethers.getContractAt("BoykaNFT",nftDeployment.address);//已经部署的合约交互
        const nftswapDeployment = await deployments.get("NFTSwap");
        nftswap = await ethers.getContractAt("NFTSwap",nftswapDeployment.address);//已经部署的合约交互
    })
    describe("nftswap",function(){
        it("把nft授权给交易所swap",async ()=>{
            //铸造两个nft
            await nft.safeMint(firstAccount,mekadata);
            console.log(await nft.ownerOf(0))//查看持有者
            await nft.safeMint(firstAccount,mekadata1);
            console.log(await nft.ownerOf(1))//同上
            //把铸造的nft授权给nft交易所
            await nft.approve(nftswap.address,0);
            await nft.approve(nftswap.address,1);
            //上架nft
            //把nft放到交易所 参数说明:nft的合约地址 ,nftid,nft的价格
            await nftswap.list(nft.address,0,ethers.utils.parseEther("1"));
            await nftswap.list(nft.address,1,ethers.utils.parseEther("1"));
            //查看是否上架成功
            console.log('上架成功',await nftswap.nftList(nft.address,0))
            //如果不存在的nft会返回owner: '0x0000000000000000000000000000000000000000',
            console.log("查看不存在的nft",await nftswap.nftList(nft.address,3))//不存在的nft
            //更新nft的价格
            await nftswap.update(nft.address,0,ethers.utils.parseEther("2"));//把nft的价格改成2eth
            console.log("查看更新的价格",await nftswap.nftList(nft.address,0))
            await nftswap.update(nft.address,1,ethers.utils.parseEther("23"));//同上
            console.log("查看更新的价格",await nftswap.nftList(nft.address,1))
            //下架
            //把nftid为1的nft下架
            await nftswap.revoke(nft.address,1);
            //返回owner为0x0000000000000000000000000000000000000000说明下架成功
            console.log('下架成功',await nftswap.nftList(nft.address,1))
            //购买nft
            console.log(secondAccount)
            //切换账号到addr2使用await ethers.getSigners() 添加ether足够的值
            //说明参数:1.nft合约地址2.nft的id3.addr2的msg.value的eth,要大于nft的价格当前addr2的值为3
           await nftswap.connect(addr2).purchase(nft.address,0,{value:ethers.utils.parseEther("3")});
           console.log('所有权时addr2',await nft.ownerOf(0));//返回的账号地址时addr2说明addr2购买成功了
        })
    })
})
# 测试指令
# npx hardhat test ./test/xxx.js

NFT交易所部署

javascript 复制代码
module.exports = async function ({getNamedAccounts,deployments}) {
    const  firstAccount= (await getNamedAccounts()).firstAccount;
    const {deploy,log} = deployments;
    const NftSwap=await deploy("NFTSwap",{
        from:firstAccount,
        args: [],//参数
        log: true,
    })
    console.log("nftswap合约",NftSwap.address)

}
module.exports.tags = ["all","nftswap"];
# 部署指令
# npx hardhat deploy

总结

以上就是简洁版NFT交易所的整个实现过程,此交易所具备上架、下架、改价、购买的功能和一些验证的方法。

相关推荐
木西2 小时前
快速实现一个去中心交易所的智能合约
web3·区块链·智能合约
GISer_Jing4 小时前
实景三维&点云处理专业软件&ArcGIS根据DSM生成地表点云集
python·arcgis·web3
CESS_Cloud4 小时前
CESS 的 2024:赋能 AI,塑造去中心化数据基础
人工智能·web3·去中心化·区块链
电报号dapp1196 小时前
数字货币支付系统开发搭建:构建未来的区块链支付生态
去中心化·区块链·智能合约
dingzd951 天前
智能合约在Web3中的作用:去中心化应用的基石
web3·去中心化·区块链·智能合约
本郡主是喵5 天前
基于FISCO BCOS的电子签章系统
区块链·智能合约·区块链系统案例
koko爱英语6 天前
区块链web3 基础知识,包括ABI、EIP、ERC等
web3·区块链
电报号dapp1196 天前
链游破局之路:如何打破边缘化,获得更好的发展
人工智能·去中心化·区块链·智能合约
电报号dapp1196 天前
现货量化合约跟单系统开发策略指南
去中心化·区块链·智能合约