ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

在以太坊上搭建NFT的交易市场

2021-07-02 11:57:41  阅读:212  来源: 互联网

标签:const tokenId price 以太 NFT uint new 搭建


NFT的介绍

NFT全称为Non-Fungible Token,非同质化代币,是用于表示数字资产的唯一加密货币令牌,具有不可分割、不可代替、独一无二等特点。在区块链上,数字加密货币分为原生币和代币两大类。原生币如大家熟悉的比特币、以太币等,拥有自己的主链,使用链上的交易来维护账本数据。代币是依附于现有的区块链,使用智能合约来进行账本的记录,如依附于以太坊上而发布的token。代币之中又可分为同质化和非同质化两种。同质化代币即FT(Fungible Token),互相可以替代、可接近无限拆分的token。例如,你手里有一个比特币与我手里的一个比特币,本质上没有任何区别,这就是同质化,就是同质化币[1]。

①NFT非同质化代币的应用:NFT非同质化代币由于其非同质化、不可拆分的特性,使得它可以和现实世界中的一些商品绑定。换言之,其实就是发行在区块链上的数字资产,这个资产可以是游戏道具、数字艺术品、门票等,并且具有唯一性和不可复制性。由于NFT具备天然的收藏属性和便于交易,加密艺术家们可以利用NFT创造出独一无二的数字艺术品[2]。
②NFT非同质化代币的特点:不可分割:NFT非同质化代币是唯一的、且不可分割成更小的单位。任何一枚比特币、以太坊等都可以换成另外一枚 比特币或以太坊,且他们都可以分割成更小的单位,例如0.1btc或者0.01btc。独一无二:NFT非同质化代币是不可互换的资产,它具有独一无二的属性。这意味着,如果NFT资产本身是稀缺的,它也会承载其价值,不可篡改或者复制。透明度:买方可以看到每个过往卖家和投标者的历史,他们付出了什么价格,以及任何数字商品的当下市场状况。

在这之前,有使用Chainlink VRF使用可验证的 RNG 构建的NFT,他创建根据现实世界数据变化的动态 NFT。通过使用去中心化的预言机来获取数据,将随机NFT添加到OpenSea中[2]。

数字化交易市场搭建

入门
建立数字化交易市场需要做的第一件事是编写智能合约。
市场将由两个主要的智能合约组成:
①用于铸造 NFT 的 NFT 合约
②促进 NFT 销售的市场合约
为了编写 NFT,我们可以使用可通过OpenZeppelin获得的ERC721标准。
首先,转到https://remix.ethereum.org/并创建一个名为Marketplace.sol的新文件。
可以通过导入从 Open Zeppelin 开始所需的合约来开始:

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.3;

import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import "hardhat/console.sol";

创建 NFT 合约

contract NFT is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    address contractAddress;

    constructor(address marketplaceAddress) ERC721("Eat The Blocks NFTs", "ETBNFT") {
        contractAddress = marketplaceAddress;
    }

    function createToken(string memory tokenURI) public returns (uint) {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();

        _mint(msg.sender, newItemId);
        _setTokenURI(newItemId, tokenURI);
        setApprovalForAll(contractAddress, true);
        return newItemId;
    }
}

构造函数接受marketplaceAddress地址的参数,保存值并使其在智能合约中可用。这样当有人想创建合约时,合约可以允许市场合约批准将代币从所有者转移到卖家。该newItemId值是从函数返回的,因为我们将在我们的客户端应用程序中需要它来了解tokenId智能合约生成的动态值。

创建市场合约
市场合约比NFT合约更为复杂,但也是该项目的重要部分。

contract NFTMarket is ReentrancyGuard {
  using Counters for Counters.Counter;
  Counters.Counter private _itemIds;
  Counters.Counter private _itemsSold;

  struct MarketItem {
    uint itemId;
    address nftContract;
    uint256 tokenId;
    address payable seller;
    address payable owner;
    uint256 price;
  }

  mapping(uint256 => MarketItem) private idToMarketItem;

  event MarketItemCreated (
    uint indexed itemId,
    address indexed nftContract,
    uint256 indexed tokenId,
    address seller,
    address owner,
    uint256 price
  );

  function getMarketItem(uint256 marketItemId) public view returns (MarketItem memory) {
    return idToMarketItem[marketItemId];
  }

  function createMarketItem(
    address nftContract,
    uint256 tokenId,
    uint256 price
  ) public payable nonReentrant {
    require(price > 0, "Price must be at least 1 wei");

    _itemIds.increment();
    uint256 itemId = _itemIds.current();
  
    idToMarketItem[itemId] =  MarketItem(
      itemId,
      nftContract,
      tokenId,
      payable(msg.sender),
      payable(address(0)),
      price
    );

    IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);

    emit MarketItemCreated(
      itemId,
      nftContract,
      tokenId,
      msg.sender,
      address(0),
      price
    );
  }

  function createMarketSale(
    address nftContract,
    uint256 itemId
    ) public payable nonReentrant {
    uint price = idToMarketItem[itemId].price;
    uint tokenId = idToMarketItem[itemId].tokenId;
    require(msg.value == price, "Please submit the asking price in order to complete the purchase");

    idToMarketItem[itemId].seller.transfer(msg.value);
    IERC721(nftContract).transferFrom(address(this), msg.sender, tokenId);
    idToMarketItem[itemId].owner = payable(msg.sender);
    _itemsSold.increment();
  }

  function fetchMarketItem(uint itemId) public view returns (MarketItem memory) {
    MarketItem memory item = idToMarketItem[itemId];
    return item;
  }

  function fetchMarketItems() public view returns (MarketItem[] memory) {
    uint itemCount = _itemIds.current();
    uint unsoldItemCount = _itemIds.current() - _itemsSold.current();
    uint currentIndex = 0;

    MarketItem[] memory items = new MarketItem[](unsoldItemCount);
    for (uint i = 0; i < itemCount; i++) {
      if (idToMarketItem[i + 1].owner == address(0)) {
        uint currentId = idToMarketItem[i + 1].itemId;
        MarketItem storage currentItem = idToMarketItem[currentId];
        items[currentIndex] = currentItem;
        currentIndex += 1;
      }
    }
   
    return items;
  }

  function fetchMyNFTs() public view returns (MarketItem[] memory) {
    uint totalItemCount = _itemIds.current();
    uint itemCount = 0;
    uint currentIndex = 0;

    for (uint i = 0; i < totalItemCount; i++) {
      if (idToMarketItem[i + 1].owner == msg.sender) {
        itemCount += 1;
      }
    }

    MarketItem[] memory items = new MarketItem[](itemCount);
    for (uint i = 0; i < totalItemCount; i++) {
      if (idToMarketItem[i + 1].owner == msg.sender) {
        uint currentId = idToMarketItem[i + 1].itemId;
        MarketItem storage currentItem = idToMarketItem[currentId];
        items[currentIndex] = currentItem;
        currentIndex += 1;
      }
    }
   
    return items;
  }
}

继承来自ReentrancyGuard的nonReentrant,它可以应用于函数以确保没有对它们的嵌套(可重入)调用。我们在MarketItem的数据据结构体中储存我们想要在市场上提供的项目的记录。idToMarketltem是一种可以允许我们在IDs和MarketIItem之间创建关键值得映射。createMarketItem得作用是把NFT转移到市场上的交易地址,这样NFT就可以出售了。createMarketSale是作为NFT和Eth之间的介质,使他们可以在交易中进行转换。fetchMarketItems可以帮助你查看所有在市场上出售的NFT。fetchMyNFTs则可以查看你的购买记录。

构建前端
在前端的构造中使用了已有的初始项目。
marketplace_starter

构建用户界面
构建用户界面是本数字化市场的重点项目,包括以下功能:获取NFT、创建NFT并将其出售、允许用户购买NFT以及允许用户查看他们自己的NFT。
loadNFTs函数通过调用fetchMarkItems来返回市场中未出售的NFT。

async function loadNFTs() {
  const provider = new ethers.providers.JsonRpcProvider()
  const tokenContract = new ethers.Contract(nftaddress, NFT.abi, provider)
  const marketContract = new ethers.Contract(nftmarketaddress, Market.abi, provider)
  const data = await marketContract.fetchMarketItems()
  
  const items = await Promise.all(data.map(async i => {
    const tokenUri = await tokenContract.tokenURI(i.tokenId)
    const meta = await axios.get(tokenUri)
    let price = web3.utils.fromWei(i.price.toString(), 'ether');
    let item = {
      price,
      tokenId: i.tokenId.toNumber(),
      seller: i.seller,
      owner: i.owner,
      image: meta.data.image,
    }
    return item
  }))
  setNfts(items)
  setLoaded('loaded') 
}

Creating an NFT and placing it for sale用于创造NFT并将其投放市场,包括createToken和createMarketItem来创建新得Token和放置出售的项目。

async function buyNft(nft) {
  const web3Modal = new Web3Modal({
    network: "mainnet",
    cacheProvider: true,
  });
  const connection = await web3Modal.connect()
  const provider = new ethers.providers.Web3Provider(connection)
  const signer = provider.getSigner()
  const contract = new ethers.Contract(nftmarketaddress, Market.abi, signer)
  
  const price = web3.utils.toWei(nft.price.toString(), 'ether');
  
  const transaction = await contract.createMarketSale(nftaddress, nft.tokenId, {
    value: price
  })
  await transaction.wait()
  loadNFTs()
}

关键的一步是允许用户从市场上购买NFT

async function buyNft(nft) {
  const web3Modal = new Web3Modal({
    network: "mainnet",
    cacheProvider: true,
  });
  const connection = await web3Modal.connect()
  const provider = new ethers.providers.Web3Provider(connection)
  const signer = provider.getSigner()
  const contract = new ethers.Contract(nftmarketaddress, Market.abi, signer)
  
  const price = web3.utils.toWei(nft.price.toString(), 'ether');
  
  const transaction = await contract.createMarketSale(nftaddress, nft.tokenId, {
    value: price
  })
  await transaction.wait()
  loadNFTs()
}

Allowing a user to view their own NFTs实现了让用户可以查看他们购买的NFT,从fetchMyNFTs智能合约中调用了该函数。

async function loadNFTs() {
  const web3Modal = new Web3Modal({
    network: "mainnet",
    cacheProvider: true,
  });
  const connection = await web3Modal.connect()
  const provider = new ethers.providers.Web3Provider(connection)
  const signer = provider.getSigner()
    
  const marketContract = new ethers.Contract(nftmarketaddress, Market.abi, signer)
  const tokenContract = new ethers.Contract(nftaddress, NFT.abi, provider)
  const data = await marketContract.fetchMyNFTs()
  
  const items = await Promise.all(data.map(async i => {
    const tokenUri = await tokenContract.tokenURI(i.tokenId)
    const meta = await axios.get(tokenUri)
    let price = web3.utils.fromWei(i.price.toString(), 'ether');
    let item = {
      price,
      tokenId: i.tokenId.toNumber(),
      seller: i.seller,
      owner: i.owner,
      image: meta.data.image,
    }
    return item
  }))

  setNfts(items)
  setLoaded('loaded') 
}

最后就是我们开发交易市场的盈利项目,从交易中收取上市费用,这仅需几行代码就可以完成。首先在Contracts中打开NFTMarkets.sol文件,需要设置使用上市的价格,还需要创建一个变量来存储合约的所有者。
可以在_itemsSold初始化以下代码:

address payable owner;
uint256 listingPrice = 0.01 ether;

然后创建一个constructor来存储地址:

constructor() {
  owner = payable(msg.sender);
}

在createMarketItem功能在,确保上市的费用包含了手续费:

require(price > 0, "Price must be at least 1 wei"); // below this line 

标签:const,tokenId,price,以太,NFT,uint,new,搭建
来源: https://blog.csdn.net/weixin_44560200/article/details/118338021

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有