在 Polygon 网络上创建全栈 NFT 市场

公众号:元壤

元壤:一分钟搭建您专属的 NFT 数字藏品平台

了解如何使用安全帽、IPFS 和 Next js 创建 NFT 市场

img

市场功能

当用户出售 NFT 时,NFT 的所有权从创建者转移到市场合约。

当用户购买 NFT 时,购买价格从买方转移到卖方,因此该项目将从市场转移到买方。

市场所有者可以设置上市费用。这笔费用从卖方收取,并在任何销售完成后转移给合约所有者,使市场所有者能够从市场上进行的任何销售中获得经常性收入。

我们将在 Polygon Network 上部署智能合约。

Polygon 如何工作?

Polygon 是一个多层次的平台,其目的是通过大量的侧链来扩展以太坊,所有这些都旨在以有效且经济的方式与主平台解除连接。

Polygon 的主链是权益证明 (PoS) 侧链,网络参与者可以在其中使用 MATIC 代币以验证交易并对网络升级进行投票。

设置环境

  1. 创建一个 next-js 应用程序
npx create-next-app marketplace

2.安装包

npm install ethers hardhat ethereum-waffle chai @nomiclabs/hardhat-waffle @nomiclabs/hardhat-ethers web3modal @openzeppelin/contracts ipfs-http-client axios react-toastify

后端

3.hardhat

npx hardhat

如果 README.md 文件出现错误,请删除并重试

4.按照给定的方式编辑 hardhat.config.js 文件:

require("@nomiclabs/hardhat-waffle");
const fs = require('fs');

module.exports = {
  defaultNetwork: "hardhat",
  networks: {
    hardhat: {
      chainId: 1337
    },
    // polygon testnet
    mumbai: {
      url: "https://rpc-mumbai.maticvigil.com",
      accounts: [process.env.privateKey] // your wallet private key
    },
    //polygon mainnet
    matic: {
      url: "https://rpc-mainnet.maticvigil.com",
      accounts: [process.env.privateKey]
    }
  },
  solidity: {
    version: "0.8.4",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  }
};

5.在contracts文件夹下创建NFTMarketplace.sol文件:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

contract NFTMarketplace is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    Counters.Counter private _itemsSold;

    /* platform fee */
    uint256 listingPrice = 0 ether; 
    address payable owner;

    mapping(uint256 => MarketItem) private idToMarketItem;

    struct MarketItem {
      uint256 tokenId;
      address payable seller;
      address payable owner;
      uint256 price;
      bool sold;
    }

    event MarketItemCreated (
      uint256 indexed tokenId,
      address seller,
      address owner,
      uint256 price,
      bool sold
    );

    constructor() ERC721("Metaverse Tokens", "METT") {
      owner = payable(msg.sender);
    }

    /* Updates the listing price of the contract */
    function updateListingPrice(uint _listingPrice) public payable {
      require(owner == msg.sender, "Only marketplace owner can update listing price.");
      listingPrice = _listingPrice;
    }

    /* Returns the listing price of the contract */
    function getListingPrice() public view returns (uint256) {
      return listingPrice;
    }

    /* Mints a token and lists it in the marketplace */
    function createToken(string memory tokenURI, uint256 price) public payable returns (uint) {
      _tokenIds.increment();
      uint256 newTokenId = _tokenIds.current();

      _mint(msg.sender, newTokenId);
      _setTokenURI(newTokenId, tokenURI);
      createMarketItem(newTokenId, price);
      return newTokenId;
    }

    function createMarketItem(
      uint256 tokenId,
      uint256 price
    ) private {
      require(price > 0, "Price must be at least 1 wei");
      require(msg.value == listingPrice, "Price must be equal to listing price");

      idToMarketItem[tokenId] =  MarketItem(
        tokenId,
        payable(msg.sender),
        payable(address(this)),
        price,
        false
      );

      _transfer(msg.sender, address(this), tokenId);
      emit MarketItemCreated(
        tokenId,
        msg.sender,
        address(this),
        price,
        false
      );
    }

    /* allows someone to resell a token they have purchased */
    function resellToken(uint256 tokenId, uint256 price) public payable {
      require(idToMarketItem[tokenId].owner == msg.sender, "Only item owner can perform this operation");
      require(msg.value == listingPrice, "Price must be equal to listing price");
      idToMarketItem[tokenId].sold = false;
      idToMarketItem[tokenId].price = price;
      idToMarketItem[tokenId].seller = payable(msg.sender);
      idToMarketItem[tokenId].owner = payable(address(this));
      _itemsSold.decrement();

      _transfer(msg.sender, address(this), tokenId);
    }

    /* Creates the sale of a marketplace item */
    /* Transfers ownership of the item, as well as funds between parties */
    function createMarketSale(
      uint256 tokenId
      ) public payable {
      uint price = idToMarketItem[tokenId].price;
      address seller = idToMarketItem[tokenId].seller;
      require(msg.value == price, "Please submit the asking price in order to complete the purchase");
      idToMarketItem[tokenId].owner = payable(msg.sender);
      idToMarketItem[tokenId].sold = true;
      idToMarketItem[tokenId].seller = payable(address(0));
      _itemsSold.increment();
      _transfer(address(this), msg.sender, tokenId);
      payable(owner).transfer(listingPrice);
      payable(seller).transfer(msg.value);
    }

    /* Returns all unsold market items */
    function fetchMarketItems() public view returns (MarketItem[] memory) {
      uint itemCount = _tokenIds.current();
      uint unsoldItemCount = _tokenIds.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(this)) {
          uint currentId = i + 1;
          MarketItem storage currentItem = idToMarketItem[currentId];
          items[currentIndex] = currentItem;
          currentIndex += 1;
        }
      }
      return items;
    }

    /* Returns only items that a user has purchased */
    function fetchMyNFTs() public view returns (MarketItem[] memory) {
      uint totalItemCount = _tokenIds.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 = i + 1;
          MarketItem storage currentItem = idToMarketItem[currentId];
          items[currentIndex] = currentItem;
          currentIndex += 1;
        }
      }
      return items;
    }

    /* Returns only items a user has listed */
    function fetchItemsListed() public view returns (MarketItem[] memory) {
      uint totalItemCount = _tokenIds.current();
      uint itemCount = 0;
      uint currentIndex = 0;

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

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

6.在 scripts 文件夹中创建 deploy.js 脚本:

const hre = require("hardhat");
const fs = require('fs');

async function main() {
  const NFTMarketplace = await hre.ethers.getContractFactory("NFTMarketplace");
  const nftMarketplace = await NFTMarketplace.deploy();
  await nftMarketplace.deployed();
  console.log("nftMarketplace deployed to:", nftMarketplace.address);

  fs.writeFileSync('./config.js', `
  export const marketplaceAddress = "${nftMarketplace.address}"
  `)
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

7.创建config.js文件

8.现在我们在 Polygon Mumbai 测试网上部署智能合约

npx hardhat run scripts/deploy.js --network mumbai

部署智能合约后,您可以看到您的市场地址

如果您在此处没有请求https://faucet.polygon.technology/ ,为了成功部署合约,您必须在您的帐户中测试 Matic

前端

在这一部分中,我们将看到如何将部署的智能合约与下一个 js 前端连接起来。

  1. 设置 Tailwind CSS
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
npx tailwindcss init -p

2.配置路径tailwind.config.js

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

3.删除 styles/globals.css 中原来的代码,并更新为如下内容:

@tailwind base;
@tailwind components;
@tailwind utilities;

4.转到 pages 文件夹并创建4个.js文件,create.jsmyNft.jsdashboard.jsresellNft.js

5.现在让我们创建 routes,打开_app.js 并按照如下内容进行更新:

import '../styles/globals.css'
import Link from 'next/link'

function MyApp({ Component, pageProps }) {
  return (
    <div>
      <nav className="border-b p-6">
        <p className="text-4xl font-bold">Metaverse Marketplace</p>
        <div className="flex mt-4">
          <Link href="/">
            <a className="mr-4">
              Home
            </a>
          </Link>
          <Link href="/create">
            <a className="mr-6">
              Create NFT
            </a>
          </Link>
          <Link href="/nftowns">
            <a className="mr-6">
              My NFTs
            </a>
          </Link>
          <Link href="/dashboard">
            <a className="mr-6">
              Dashboard
            </a>
          </Link>
        </div>
      </nav>
      <Component {...pageProps} />
    </div>
  )
}

export default MyApp

6.打开index.js并编辑如下:

import { ethers } from 'ethers'
import { useEffect, useState } from 'react'
import axios from 'axios'
import Web3Modal from 'web3modal'

import {
  marketplaceAddress
} from '../config'

import NFTMarketplace from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json'

export default function Home() {
  const [nfts, setNfts] = useState([])
  const [loadingState, setLoadingState] = useState('not-loaded')
  useEffect(() => {
    loadNFTs()
  }, [])

  /*
  *  map over items returned from smart contract and format 
  *  them as well as fetch their token metadata
  */
  async function loadNFTs() {
    /* create a generic provider and query for unsold market items */
    const provider = new ethers.providers.JsonRpcProvider("https://rpc-mumbai.maticvigil.com")
    //console.log('1.1. provider------Success', provider)

    //console.log('1.02. marketplaceAddress, NFTMarketplace.abi, provider----', marketplaceAddress, NFTMarketplace.abi, provider)
    const contract = new ethers.Contract(marketplaceAddress, NFTMarketplace.abi, provider)
    //console.log('1.2. contract------Success', contract)

    let data = null
    try {
      data = await contract.fetchMarketItems()
      //console.log('2.1. contract.fetchMarketItems------Success', data)
    } catch (error) {
      //console.log('2.2. contract.fetchMarketItems------failed', error)
      return toast.error(error || 'Error contract.fetchMarketItems')
    }

    /*
    *  map over items returned from smart contract and format 
    *  them as well as fetch their token metadata
    */
    try {
      const items = await Promise.all(data.map(async (i) => {
        const tokenUri = await contract.tokenURI(i.tokenId)
        const meta = await axios.get(tokenUri)
        const price = ethers.utils.formatUnits(i.price.toString(), 'ether')
        const item = {
          price,
          tokenId: i.tokenId.toNumber(),
          seller: i.seller,
          owner: i.owner,
          image: meta.data.image,
          name: meta.data.name,
          description: meta.data.description,
        }
        return item
      }))
      setNfts(items)
      setLoadingState('loaded')
      //console.log('3.1. get NFT List-----------Success', items)
    } catch (error) {
      //console.log('3.2. Error get NFT List-----------', error)
      setLoadingState('loaded')
      return toast.error(error || 'Error get NFT List')
    }
  }

  async function buyNft(nft) {
    /* needs the user to sign the transaction, so will use Web3Provider and sign it */
    const web3Modal = new Web3Modal()

    let connection = null
    try {
      connection = await web3Modal.connect()
      //console.log('1.1. Connection-----------Success', connection)
    } catch (error) {
      //console.log('1.2. Connection-----------Error', error)
      return toast.error(error || 'Error Connection')
    }

    const provider = new ethers.providers.Web3Provider(connection)
    //console.log('2. provider-----------Success', provider)

    const signer = provider.getSigner()
    //console.log('3. signer-----------Success', signer)

    //console.log('4.01. marketplaceAddress, NFTMarketplace.abi, signer----', marketplaceAddress, NFTMarketplace.abi, signer)
    const contract = new ethers.Contract(marketplaceAddress, NFTMarketplace.abi, signer)
    //console.log('4.2. contract------Success', contract)

    /* user will be prompted to pay the asking proces to complete the transaction */
    const price = ethers.utils.parseUnits(nft.price.toString(), 'ether')
    //console.log('5. price------Success', price)

    let transaction = null
    try {
      transaction = await contract.createMarketSale(nft.tokenId, {
        value: price
      })
      //console.log('6.1. transaction-----------Success', transaction)
    } catch (error) {
      //console.log('6.2. transaction-----------Error', error)
      return toast.error(error || 'Error transaction')
    }

    try {
      await transaction.wait()
      loadNFTs()
      //console.log('7.1. transaction.wait-----------Success', transaction)
    } catch (error) {
      //console.log('7.2. transaction.wait-----------Error', error)
      return toast.error(error || 'Error transaction.wait')
    }
  }
  if (loadingState === 'loaded' && !nfts.length) return (<h1 className="px-20 py-10 text-3xl">No items in marketplace</h1>)
  return (
    <div className="flex justify-center">
      <div className="px-4" style={{ maxWidth: '1600px' }}>
        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 pt-4">
          {
            nfts.map((nft, i) => (
              <div key={i} className="border shadow rounded-xl overflow-hidden">
                <img src={nft.image} />
                <div className="p-4">
                  <p style={{ height: '64px' }} className="text-2xl font-semibold">{nft.name}</p>
                  <div style={{ height: '70px', overflow: 'hidden' }}>
                    <p className="text-gray-400">{nft.description}</p>
                  </div>
                </div>
                <div className="p-4 bg-black">
                  <p className="text-2xl font-bold text-white">{nft.price} ETH</p>
                  <button className="mt-4 w-full bg-pink-500 text-white font-bold py-2 px-12 rounded" onClick={() => buyNft(nft)}>Buy</button>
                </div>
              </div>
            ))
          }
        </div>
      </div>
    </div>
  )
}

7.现在编辑create.js和编写用于创建 NFT 的代码,如下所示:

import { useState } from 'react'
import { ethers } from 'ethers'
import { create as ipfsHttpClient } from 'ipfs-http-client'
import { useRouter } from 'next/router'
import Web3Modal from 'web3modal'
import { toast } from 'react-toastify'

import {
  marketplaceAddress
} from '../config'

import NFTMarketplace from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json'

export default function CreateItem() {
  const [fileUrl, setFileUrl] = useState('')
  const [formInput, updateFormInput] = useState({ price: '', name: '', description: '' })
  const router = useRouter()

  const client = ipfsHttpClient({ host: 'ipfs.infura.io', port: 5001, protocol: 'https', apiPath: 'api/v0' })

  // convert image to URL
  async function onChange(e) {
    const file = e.target.files[0]
    try {
      const added = await client.add(
        file,
        // {
        //     progress: (prog) => //console.log(`received: ${prog}`)
        // }
      )
      const url = `https://ipfs.infura.io/ipfs/${added.path}`
      setFileUrl(url)
    } catch (error) {
      //console.log('Error uploading file: ', error)
      toast.error(error || 'Error on Onchange File')
    }
  }

  // upload to ipfs and return ipfs url
  async function uploadToIPFS() {
    const { name, description, price } = formInput
    if (!name || !description || !price || !fileUrl) return
    /* first, upload to IPFS */
    const data = JSON.stringify({
      name, description, image: fileUrl
    })
    try {
      const added = await client.add(data)
      const url = `https://ipfs.infura.io/ipfs/${added.path}`
      /* after file is uploaded to IPFS, return the URL to use it in the transaction */
      return url
    } catch (error) {
      //console.log('Error uploading file: ', error)
      toast.error(error || 'Error on uploadToIPFS')
    }
  }

  async function listNFTForSale() {
    const url = await uploadToIPFS()
    //console.log('1. uploadToIPFS Success------', url)
    const web3Modal = new Web3Modal()
    //console.log('2. Web3Modal------', web3Modal)
    let connection = null
    try {
      connection = await web3Modal.connect()
      //console.log('3.1. connection------success', connection)
    } catch (error) {
      //console.log('3.2. connection------failed', error)
      toast.error(error || 'Error on web3Modal Connect')
    }

    if (!connection) return
    const provider = new ethers.providers.Web3Provider(connection)
    //console.log('4. Provider ------success', provider)
    const signer = provider.getSigner()
    //console.log('5. signer ------success', signer)

    /* next, create the item */
    if (!formInput.price) return toast.error('Please enter price')
    const price = ethers.utils.parseUnits(formInput.price, 'ether')
    //console.log('6. price ------success', price)
    //console.log('00.7. marketplaceAddress ------NFTMarketplace.abi, ---signer', marketplaceAddress, NFTMarketplace.abi, signer)

    const contract = new ethers.Contract(
      marketplaceAddress,
      NFTMarketplace.abi,
      signer
    )
    //console.log('7. contract ------success', contract)

    let listingPrice = null
    try {
      listingPrice = await contract.getListingPrice()
      listingPrice = listingPrice.toString()
      //console.log('8.1. listingPrice------success', listingPrice)
    } catch (error) {
      //console.log('8.2. listingPrice------error', error)
      toast.error(error || 'Error on getListingPrice')
    }
    if (!listingPrice) return

    let transaction = null
    try {
      transaction = await contract.createToken(url, price, { value: listingPrice })
      //console.log('9.1. contract.createToken------success', transaction)
    } catch (error) {
      //console.log('9.2. contract.createToken------error', error)
      toast.error(error?.data?.message || 'Error while creating token')
    }

    if (!transaction) return
    try {
      await transaction.wait()
      //console.log('10.1 transaction.wait------success')
    } catch (error) {
      //console.log('10.2 transaction.wait------error', error)
      toast.error(error || 'Error while transaction.wait')
    }

    //console.log(transaction);

  }

  return (
    <div className="flex justify-center">
      <div className="w-1/2 flex flex-col pb-12">
        <input
          placeholder="Asset Name"
          className="mt-8 border rounded p-4"
          onChange={e => updateFormInput({ ...formInput, name: e.target.value })}
        />
        <textarea
          placeholder="Asset Description"
          className="mt-2 border rounded p-4"
          onChange={e => updateFormInput({ ...formInput, description: e.target.value })}
        />
        <input
          placeholder="Asset Price in Eth"
          className="mt-2 border rounded p-4"
          onChange={e => updateFormInput({ ...formInput, price: e.target.value })}
        />
        <input
          type="file"
          name="Asset"
          className="p-4 w-80 rounded"
          onChange={onChange}
        />
        {
          fileUrl && (
            <div className="text-black">File Url: {fileUrl}</div>
          )
        }
        <button onClick={listNFTForSale} className="font-bold mt-4 bg-gray-500 text-white rounded p-4 shadow-lg">
          Create NFT
        </button>
      </div>
    </div>
  )
}

8.现在编辑myNft.js如下:

import { ethers } from 'ethers'
import { useEffect, useState } from 'react'
import axios from 'axios'
import Web3Modal from 'web3modal'
import { useRouter } from 'next/router'

import {
  marketplaceAddress
} from '../config'

import NFTMarketplace from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json'

export default function MyAssets() {
  const [nfts, setNfts] = useState([])
  const [loadingState, setLoadingState] = useState('not-loaded')
  const router = useRouter()
  useEffect(() => {
    loadNFTs()
  }, [])
  async function loadNFTs() {
    const web3Modal = new Web3Modal({
      network: "mainnet",
      cacheProvider: true,
    })
    // console.log('1. web3Modal-------------success', web3Modal)

    let connection = null
    try {
      connection = await web3Modal.connect()
      // console.log('2.1. connection-------------success', connection)
    } catch (error) {
      // console.log('2.2. connection-------------error', error)
      return toast.error(error || 'Error web3Modal.connect')
    }

    const provider = new ethers.providers.Web3Provider(connection)
    // console.log('3. provider-------------success', provider)

    const signer = provider.getSigner()
    // console.log('4. signer-------------success', signer)

    // console.log('5.01. marketplaceAddress, NFTMarketplace.abi, signer----', marketplaceAddress, NFTMarketplace.abi, signer)
    const marketplaceContract = new ethers.Contract(marketplaceAddress, NFTMarketplace.abi, signer)
    // console.log('5.2. marketplaceContract----------Success', marketplaceContract)

    let data = null
    try {
      data = await marketplaceContract.fetchMyNFTs()
      // console.log('6.1. marketplaceContract.fetchMyNFTs-------------success', data)
    } catch (error) {
      // console.log('6.2. marketplaceContract.fetchMyNFTs-------------error', error)
      return toast.error(error || 'Error marketplaceContract.fetchMyNFTs')
    }

    try {
      const items = await Promise.all(data.map(async (i) => {
        const tokenURI = await marketplaceContract.tokenURI(i.tokenId)
        const meta = await axios.get(tokenURI)
        const price = ethers.utils.formatUnits(i.price.toString(), 'ether')
        const item = {
          price,
          tokenId: i.tokenId.toNumber(),
          seller: i.seller,
          owner: i.owner,
          image: meta.data.image,
          tokenURI
        }
        return item
      }))
      setNfts(items)
      setLoadingState('loaded')
      // console.log('7.1. get NFT List-----------Success', items)
    } catch (error) {
      // console.log('7.2. Error get NFT List-----------', error)
      setLoadingState('loaded')
      return toast.error(error || 'Error get NFT List')
    }
  }

  function listNFT(nft) {
    // console.log('nft:', nft)
    router.push(`/nft-demo/resell-nft?id=${nft.tokenId}&tokenURI=${nft.tokenURI}`)
  }

  if (loadingState === 'loaded' && !nfts.length) return (<h1 className="py-10 px-20 text-3xl">No NFTs owned</h1>)
  return (
    <div className="flex justify-center">
      <div className="p-4">
        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 pt-4">
          {
            nfts.map((nft, i) => (
              <div key={i} className="border shadow rounded-xl overflow-hidden">
                <img src={nft.image} className="rounded" />
                <div className="p-4 bg-black">
                  <p className="text-2xl font-bold text-white">Price - {nft.price} Eth</p>
                  <button className="mt-4 w-full bg-pink-500 text-white font-bold py-2 px-12 rounded" onClick={() => listNFT(nft)}>List</button>
                </div>
              </div>
            ))
          }
        </div>
      </div>
    </div>
  )
}

9.我们对 dashboard.js 也类似:

import { ethers } from 'ethers'
import { useEffect, useState } from 'react'
import axios from 'axios'
import Web3Modal from 'web3modal'

import {
  marketplaceAddress
} from '../config'

import NFTMarketplace from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json'

export default function CreatorDashboard() {
  const [nfts, setNfts] = useState([])
  const [loadingState, setLoadingState] = useState('not-loaded')

  useEffect(() => {
    loadNFTs()
  }, [])

  async function loadNFTs() {
    const web3Modal = new Web3Modal({
      network: 'mainnet',
      cacheProvider: true,
    })
    //console.log('1. web3Modal------success', web3Modal)

    let connection = null
    try {
      connection = await web3Modal.connect()
      //console.log('2.1. connection------success', connection)
    } catch (error) {
      //console.log('2.2. connection------failed', error)
      return toast.error(error || 'Error on web3Modal Connect')
    }

    const provider = new ethers.providers.Web3Provider(connection)
    //console.log('3. Provider ------success', provider)
    const signer = provider.getSigner()
    //console.log('4. signer ------success', signer)

    //console.log('005. marketplaceAddress ------NFTMarketplace.abi, ---signer', marketplaceAddress, NFTMarketplace.abi, signer)
    const contract = new ethers.Contract(marketplaceAddress, NFTMarketplace.abi, signer)
    //console.log('5. contract ------success', contract)

    let data = null
    try {
      data = await contract.fetchItemsListed()
      //console.log('6.1. contract fetchItemsListed------success', data)
    } catch (error) {
      //console.log('6.1. contract fetchItemsListed------success', error)
      return toast.error(error || 'Error contract fetchItemsListed')
    }

    try {
      const items = await Promise.all(data.map(async (i) => {
        const tokenUri = await contract.tokenURI(i.tokenId)
        const meta = await axios.get(tokenUri)
        const price = ethers.utils.formatUnits(i.price.toString(), 'ether')
        const item = {
          price,
          tokenId: i.tokenId.toNumber(),
          seller: i.seller,
          owner: i.owner,
          image: meta.data.image,
        }
        return item
      }))
      //console.log('7.1. get Token List-----------Success', items)
      setNfts(items)
      setLoadingState('loaded')
    } catch (error) {
      //console.log('7.2. Error get List-----------', error)
      setLoadingState('loaded')
      return toast.error(error || 'Error get List')
    }
  }

  if (loadingState === 'loaded' && !nfts.length) return (
    <h1 className="text-xl p-4 w-96 my-10 mx-auto">No NFTs listed</h1>
  )
  return (
    <div>
      <div className="p-4">
        <h2 className="text-2xl py-2">Items Listed</h2>
        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 pt-4">
          {
            nfts.map((nft, i) => (
              <div key={i} className="border shadow rounded-xl overflow-hidden">
                <img src={nft.image} className="rounded" />
                <div className="p-4 bg-black">
                  <p className="text-2xl font-bold text-white">Price - {nft.price} Eth</p>
                </div>
              </div>
            ))
          }
        </div>
      </div>
    </div>
  )
}

10.最后,编辑页面resellNft.js

import { ethers } from 'ethers'
import { useEffect, useState } from 'react'
import axios from 'axios'
import Web3Modal from 'web3modal'
import { useRouter } from 'next/router'

import {
  marketplaceAddress
} from '../config'

import NFTMarketplace from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json'

export default function MyAssets() {
  const [nfts, setNfts] = useState([])
  const [loadingState, setLoadingState] = useState('not-loaded')
  const router = useRouter()
  useEffect(() => {
    loadNFTs()
  }, [])
  async function loadNFTs() {
    const web3Modal = new Web3Modal({
      network: "mainnet",
      cacheProvider: true,
    })
    // console.log('1. web3Modal-------------success', web3Modal)

    let connection = null
    try {
      connection = await web3Modal.connect()
      // console.log('2.1. connection-------------success', connection)
    } catch (error) {
      // console.log('2.2. connection-------------error', error)
      return toast.error(error || 'Error web3Modal.connect')
    }

    const provider = new ethers.providers.Web3Provider(connection)
    // console.log('3. provider-------------success', provider)

    const signer = provider.getSigner()
    // console.log('4. signer-------------success', signer)

    // console.log('5.01. marketplaceAddress, NFTMarketplace.abi, signer----', marketplaceAddress, NFTMarketplace.abi, signer)
    const marketplaceContract = new ethers.Contract(marketplaceAddress, NFTMarketplace.abi, signer)
    // console.log('5.2. marketplaceContract----------Success', marketplaceContract)

    let data = null
    try {
      data = await marketplaceContract.fetchMyNFTs()
      // console.log('6.1. marketplaceContract.fetchMyNFTs-------------success', data)
    } catch (error) {
      // console.log('6.2. marketplaceContract.fetchMyNFTs-------------error', error)
      return toast.error(error || 'Error marketplaceContract.fetchMyNFTs')
    }

    try {
      const items = await Promise.all(data.map(async (i) => {
        const tokenURI = await marketplaceContract.tokenURI(i.tokenId)
        const meta = await axios.get(tokenURI)
        const price = ethers.utils.formatUnits(i.price.toString(), 'ether')
        const item = {
          price,
          tokenId: i.tokenId.toNumber(),
          seller: i.seller,
          owner: i.owner,
          image: meta.data.image,
          tokenURI
        }
        return item
      }))
      setNfts(items)
      setLoadingState('loaded')
      // console.log('7.1. get NFT List-----------Success', items)
    } catch (error) {
      // console.log('7.2. Error get NFT List-----------', error)
      setLoadingState('loaded')
      return toast.error(error || 'Error get NFT List')
    }
  }

  function listNFT(nft) {
    // console.log('nft:', nft)
    router.push(`/nft-demo/resell-nft?id=${nft.tokenId}&tokenURI=${nft.tokenURI}`)
  }

  if (loadingState === 'loaded' && !nfts.length) return (<h1 className="py-10 px-20 text-3xl">No NFTs owned</h1>)
  return (
    <div className="flex justify-center">
      <div className="p-4">
        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 pt-4">
          {
            nfts.map((nft, i) => (
              <div key={i} className="border shadow rounded-xl overflow-hidden">
                <img src={nft.image} className="rounded" />
                <div className="p-4 bg-black">
                  <p className="text-2xl font-bold text-white">Price - {nft.price} Eth</p>
                  <button className="mt-4 w-full bg-pink-500 text-white font-bold py-2 px-12 rounded" onClick={() => listNFT(nft)}>List</button>
                </div>
              </div>
            ))
          }
        </div>
      </div>
    </div>
  )
}

运行程序

现在我们可以测试应用了!

要启动应用程序,请运行以下命令:

npm run dev

存储库

github的链接。

原文链接:https://betterprogramming.pub/create-a-full-stack-nft-marketplace-on-the-polygon-network-20176b3a9e33

相关新闻

联系我们

联系我们

133-3118-4066

在线咨询:点击这里给我发消息

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
关注微信
分享本页
返回顶部