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

  • 木西
  • 发布于 8小时前
  • 阅读 51

前言本文实现一个简单版的NFT交易所,主要包含上架、下架、更新价格、购买NFT等相关功能NFT合约说明关于NFT合约的开发、测试、部署具体实现,可以查看另一篇:快速实现一个标准的NFT合约(实操篇)NFT交易所合约合约说明具备上架、下架、更新价格、购买NFT//SPDX-Licen

前言

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

NFT合约说明

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

NFT交易所合约

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


// 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交易所测试

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交易所部署

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交易所的整个实现过程,此交易所具备上架、下架、改价、购买的功能和一些验证的方法。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
江湖只有他的大名,没有他的介绍。