# Alchemy第七周教程

By [xiaoge](https://paragraph.com/@xiaoge) · 2022-11-12

---

安装VScode和其它一些软件我就不写攻略了.
-----------------------

先决条件：用之前注册好的Alchemy账号并创建一个新的应用程序
--------------------------------

因为之前已经创建好了，所以直接用就好，没创建的点击右上角的CREATE APP进行创建。

![](https://storage.googleapis.com/papyrus_images/a6f6855752dc952dabd78a61583f75e62959ea97ac37c82e931e21f5d54d9ec1.png)

### 设置MetaMask钱包以进行开发

在小狐狸上点击添加网络，然后填入对应参数

**Network Name:** Goerli Test Network

**RPC base URL:** [https://eth-goerli.alchemyapi.io/v2/](https://eth-goerli.alchemyapi.io/v2/%7BINSERT)你创建应用的API KEY

**Chain ID:** 5

**Block Explorer URL:** [https://goerli.etherscan.io/](https://goerli.etherscan.io/)

**Symbol (Optional):** ETH

添加完成后导航到下面地址领取测试代币：

[https://goerlifaucet.com/](https://goerlifaucet.com/)

![](https://storage.googleapis.com/papyrus_images/eb6064489dd60be9359b195478f7ff058322ad57a3545b3090464c1cfa9c19c4.png)

设置存储库
-----

GitHub地址：

[https://github.com/alchemyplatform/RTW3-Week7-NFT-Marketplace](https://github.com/alchemyplatform/RTW3-Week7-NFT-Marketplace)

下载ZIP包，下载完成后解压

![](https://storage.googleapis.com/papyrus_images/0e8cfb1993f0d4e53cbfb9bd085a21ad3c2550a707408bdfb9ea12d87eb3b1ad.png)

然后我们用VSCode打开此项目

![](https://storage.googleapis.com/papyrus_images/13f036ba4707726e8c5c650268d8bea5ae2b56acadcdba17c1839c7e0e07912b.png)

![](https://storage.googleapis.com/papyrus_images/299e57243ae9df67dd9a2bc7f965820970eddc7e49600d693a611d2df1d27341.png)

![](https://storage.googleapis.com/papyrus_images/280735be8a9a07edbd2f3dd8e9f4ae80762d865d949e8e7bd5a83b6d79a4b755.png)

选择刚才解压出来的文件夹

然后在项目中打开终端输入**npm install**，等待依赖下载完成

![](https://storage.googleapis.com/papyrus_images/e7ac73ab5b415b0e41ac97292ab467ca787e9f3d66e09f088a27cabb5a41c278.png)

接着输入**npm start** ，浏览器就会弹出下面网页

![](https://storage.googleapis.com/papyrus_images/8c6637d25f1ee05a7849cc2d98d8be72918a6a970d15c9734014196a5a794d7e.png)

设置环境变量和 Hardhat 配置
------------------

我们新增一个cmd终端

![](https://storage.googleapis.com/papyrus_images/ed47777688ce6d7ba534aecc623d7c90cf2e80c99e3367a32fc07522a5463689.png)

在新的终端输入**npm install dotenv --save**，下载安装 dotenv

![](https://storage.googleapis.com/papyrus_images/142d4887748017719bbee5c255eee72e24ad084d54734bc9040a3cc218f2d75f.png)

在根目录创建.env文件

![](https://storage.googleapis.com/papyrus_images/6ee0ba7b338400798a783b52bbde8e786bafb20921ae94b0068fd6c110b024ec.png)

![](https://storage.googleapis.com/papyrus_images/8d444549d485b9ebc81733dcb472f59da2411ce8c540dce92582553ce2c409b7.png)

找到hardhat.config.js文件，并将其中代码替换

![](https://storage.googleapis.com/papyrus_images/399608b1d573767f0020409cd325a3f47041b6fc3a2d46f98cd0032bbde14d35.png)

替换代码（ctrl+s保存）

    require("@nomiclabs/hardhat-waffle");
    require("@nomiclabs/hardhat-ethers");
    const fs = require('fs');
    // const infuraId = fs.readFileSync(".infuraid").toString().trim() || "";
    require('dotenv').config();
    
    task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
      const accounts = await hre.ethers.getSigners();
    
      for (const account of accounts) {
        console.log(account.address);
      }
    });
    
    module.exports = {
      defaultNetwork: "hardhat",
      networks: {
        hardhat: {
          chainId: 1337
        },
        goerli: {
          url: process.env.REACT_APP_ALCHEMY_API_URL,
          accounts: [ process.env.REACT_APP_PRIVATE_KEY ]
        }
      },
      solidity: {
        version: "0.8.4",
        settings: {
          optimizer: {
            enabled: true,
            runs: 200
          }
        }
      }
    };
    

使用 Piñata 将数据上传到 IPFS
---------------------

导航到下面地址注册并登录

[https://app.pinata.cloud/signup](https://app.pinata.cloud/signup)

创建key

![](https://storage.googleapis.com/papyrus_images/b9e6b9f03a55f90f811a3d3ba9f27dd06f05aaf5f7a8bde6cee034756d4b3249.png)

点击创建，然后会弹出一个窗口，将其中的信息复制

![](https://storage.googleapis.com/papyrus_images/aec2ea03922523a46bc43914d0b7d55da158ea80ce38fee58e3501e2986d2a29.png)

![](https://storage.googleapis.com/papyrus_images/385af2fdeb2a1b56c686aa0dd8a9d0cbd415dd71498d111d28505a67cb2d98b0.png)

在.env中添加配置(记得保存)

    REACT_APP_PINATA_KEY="<YOUR_PINATA_KEY>"  #上图的API Key
    REACT_APP_PINATA_SECRET="<YOUR_PINATA_SECRET>"  #上图的API Secret
    

![](https://storage.googleapis.com/papyrus_images/ab3c40ccf2c1bdccb6a681b8e6ab61edceb4845555ea67833c21b89badf08d2e.png)

![](https://storage.googleapis.com/papyrus_images/5a598940ef99cb5ae24899649ee41449b7ce38a61c1b74a6b966bdd2da402e55.png)

编写智能合约
------

打开NFTMarketplace.sol文件，替换代码

![](https://storage.googleapis.com/papyrus_images/a6e65e173146d1ca69b4cea84c72d657f7a05c1047db1e4d8489af32789b5ca0.png)

替换代码：替换完记得保存

    //SPDX-License-Identifier: Unlicense
    pragma solidity ^0.8.0;
    
    //Console functions to help debug the smart contract just like in Javascript
    import "hardhat/console.sol";
    //OpenZeppelin's NFT Standard Contracts. We will extend functions from this in our implementation
    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;
        //对应于使用此智能合约铸造的 NFT 的最新代币 ID。tokenIDs 映射到的是包含相应 NFT 元数据的 URL
        Counters.Counter private _tokenIds;
        //对市场上出售的商品数量的计数
        Counters.Counter private _itemsSold;
        //智能合约的所有者
        address payable owner;
        //任何用户在市场上列出其 NFT 所需支付的价格
        uint256 listPrice = 0.01 ether;
    
        //指示 NFT 数据的存储格式
        struct ListedToken {
            uint256 tokenId;
            address payable owner;
            address payable seller;
            uint256 price;
            bool currentlyListed;
        }
    
        //成功列出Token时发出的事件
        event TokenListedSuccess (
            uint256 indexed tokenId,
            address owner,
            address seller,
            uint256 price,
            bool currentlyListed
        );
    
        //所有现有 tokenId 到对应 NFT 代币的映射
        mapping(uint256 => ListedToken) private idToListedToken;
    
        constructor() ERC721("NFTMarketplace", "NFTM") {
            owner = payable(msg.sender);
        }
    
            //The first time a token is created, it is listed here
        function createToken(string memory tokenURI, uint256 price) public payable returns (uint) {
            //Increment the tokenId counter, which is keeping track of the number of minted NFTs
            _tokenIds.increment();
            uint256 newTokenId = _tokenIds.current();
    
            //Mint the NFT with tokenId newTokenId to the address who called createToken
            _safeMint(msg.sender, newTokenId);
    
            //Map the tokenId to the tokenURI (which is an IPFS URL with the NFT metadata)
            _setTokenURI(newTokenId, tokenURI);
    
            //Helper function to update Global variables and emit an event
            createListedToken(newTokenId, price);
    
            return newTokenId;
        }
    
        function createListedToken(uint256 tokenId, uint256 price) private {
            //Make sure the sender sent enough ETH to pay for listing
            require(msg.value == listPrice, "Hopefully sending the correct price");
            //Just sanity check
            require(price > 0, "Make sure the price isn't negative");
    
            //Update the mapping of tokenId's to Token details, useful for retrieval functions
            idToListedToken[tokenId] = ListedToken(
                tokenId,
                payable(address(this)),
                payable(msg.sender),
                price,
                true
            );
    
            _transfer(msg.sender, address(this), tokenId);
            //Emit the event for successful transfer. The frontend parses this message and updates the end user
            emit TokenListedSuccess(
                tokenId,
                address(this),
                msg.sender,
                price,
                true
            );
        }
    
        //获取所有 NFT
        function getAllNFTs() public view returns (ListedToken[] memory) {
            uint nftCount = _tokenIds.current();
            ListedToken[] memory tokens = new ListedTokenUnsupported embed;
            uint currentIndex = 0;
    
            //at the moment currentlyListed is true for all, if it becomes false in the future we will 
            //filter out currentlyListed == false over here
            for(uint i=0;i<nftCount;i++)
            {
                uint currentId = i + 1;
                ListedToken storage currentItem = idToListedToken[currentId];
                tokens[currentIndex] = currentItem;
                currentIndex += 1;
            }
            //the array 'tokens' has the list of all NFTs in the marketplace
            return tokens;
        }
    
        //获取MyNFT
        function getMyNFTs() public view returns (ListedToken[] memory) {
            uint totalItemCount = _tokenIds.current();
            uint itemCount = 0;
            uint currentIndex = 0;
            
            //Important to get a count of all the NFTs that belong to the user before we can make an array for them
            for(uint i=0; i < totalItemCount; i++)
            {
                if(idToListedToken[i+1].owner == msg.sender || idToListedToken[i+1].seller == msg.sender){
                    itemCount += 1;
                }
            }
    
            //Once you have the count of relevant NFTs, create an array then store all the NFTs in it
            ListedToken[] memory items = new ListedTokenUnsupported embed;
            for(uint i=0; i < totalItemCount; i++) {
                if(idToListedToken[i+1].owner == msg.sender || idToListedToken[i+1].seller == msg.sender) {
                    uint currentId = i+1;
                    ListedToken storage currentItem = idToListedToken[currentId];
                    items[currentIndex] = currentItem;
                    currentIndex += 1;
                }
            }
            return items;
        }
    
        /**
            当用户在个人资料页面点击“购买此 NFT”时，该功能被触发
            如果用户支付了与 NFT 价格相等的 ETH，NFT 将被转移到新地址，销售收益将发送给卖家。
         */
        function executeSale(uint256 tokenId) public payable {
            uint price = idToListedToken[tokenId].price;
            address seller = idToListedToken[tokenId].seller;
            require(msg.value == price, "Please submit the asking price in order to complete the purchase");
    
            //update the details of the token
            idToListedToken[tokenId].currentlyListed = true;
            idToListedToken[tokenId].seller = payable(msg.sender);
            _itemsSold.increment();
    
            //Actually transfer the token to the new owner
            _transfer(address(this), msg.sender, tokenId);
            //approve the marketplace to sell NFTs on your behalf
            approve(address(this), tokenId);
    
            //Transfer the listing fee to the marketplace creator
            payable(owner).transfer(listPrice);
            //Transfer the proceeds from the sale to the seller of the NFT
            payable(seller).transfer(msg.value);
        }
    
        //以下都为辅助函数，测试合约功能
        function updateListPrice(uint256 _listPrice) public payable {
            require(owner == msg.sender, "Only owner can update listing price");
            listPrice = _listPrice;
        }
    
        function getListPrice() public view returns (uint256) {
            return listPrice;
        }
    
        function getLatestIdToListedToken() public view returns (ListedToken memory) {
            uint256 currentTokenId = _tokenIds.current();
            return idToListedToken[currentTokenId];
        }
    
        function getListedTokenForId(uint256 tokenId) public view returns (ListedToken memory) {
            return idToListedToken[tokenId];
        }
    
        function getCurrentToken() public view returns (uint256) {
            return _tokenIds.current();
        }
    
    }
    

在 Goerli 上部署智能合约
----------------

打开deploy.js文件，替换代码

![](https://storage.googleapis.com/papyrus_images/49ffe844019cd55c716c1552ecafad73e47dbbbce5cacbddc8f3ed58bc04f61d.png)

替换代码：记得保存

    const { ethers } = require("hardhat");
    const hre = require("hardhat");
    const fs = require("fs");
    
    async function main() {
      //get the signer that we will use to deploy
      const [deployer] = await ethers.getSigners();
      
      //Get the NFTMarketplace smart contract object and deploy it
      const Marketplace = await hre.ethers.getContractFactory("NFTMarketplace");
      const marketplace = await Marketplace.deploy();
    
      await marketplace.deployed();
      
      //Pull the address and ABI out while you deploy, since that will be key in interacting with the smart contract later
      const data = {
        address: marketplace.address,
        abi: JSON.parse(marketplace.interface.format('json'))
      }
    
      //This writes the ABI and address to the marketplace.json
      //This data is then used by frontend files to connect with the smart contract
      fs.writeFileSync('./src/Marketplace.json', JSON.stringify(data))
    }
    
    main()
      .then(() => process.exit(0))
      .catch((error) => {
        console.error(error);
        process.exit(1);
      });
    

先强行把私钥和url写入代码里（记得保存不然不生效）

![](https://storage.googleapis.com/papyrus_images/0e1dd8e8d5b450f57ac257e6c2257b35e5f0f2f1da2567736535ce1da883f99c.png)

![](https://storage.googleapis.com/papyrus_images/e3419da495cdda58517b527652041483bcd4e19e555b4a5b784a15dd3019c4a0.png)

我们输入

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

部署合约，部署完成后我们可以在marketplace.json文件中查看合约地址和ABI。

当你看到生成了一下的文件则说明你已经成功部署了

![](https://storage.googleapis.com/papyrus_images/a3fc4a78741c2cf59ee167e1be67dffc11644b0dc5cd3a8dbe5c5593bb1bc74b.png)

我们在部署的json文件中也可以找到合约的地址，我们去浏览器查看下帝哥部署的合约地址是：0xa8b8DF1472CC853137483DF139e0c08e7C8F5035

![](https://storage.googleapis.com/papyrus_images/67d14dcf701a0d1be2cb79e76a98790c571f24f62f239f56232fcbe283666cf9.png)

添加将 NFT 元数据上传到 Piñata 的功能
-------------------------

将pinata.js文件中的代码替换

![](https://storage.googleapis.com/papyrus_images/21cdc1d1ad9eb7ebedd7d7d6a3ff72b2786b30cc195d65110af203dc8c7532eb.png)

替换代码：记得保存

    //require('dotenv').config();
    const key = process.env.REACT_APP_PINATA_KEY;
    const secret = process.env.REACT_APP_PINATA_SECRET;
    
    const axios = require('axios');
    const FormData = require('form-data');
    
    export const uploadJSONToIPFS = async(JSONBody) => {
        const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`;
        //making axios POST request to Pinata ⬇️
        return axios 
            .post(url, JSONBody, {
                headers: {
                    pinata_api_key: key,
                    pinata_secret_api_key: secret,
                }
            })
            .then(function (response) {
               return {
                   success: true,
                   pinataURL: "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash
               };
            })
            .catch(function (error) {
                console.log(error)
                return {
                    success: false,
                    message: error.message,
                }
    
        });
    };
    
    export const uploadFileToIPFS = async(file) => {
        const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
        //making axios POST request to Pinata ⬇️
        
        let data = new FormData();
        data.append('file', file);
    
        const metadata = JSON.stringify({
            name: 'testname',
            keyvalues: {
                exampleKey: 'exampleValue'
            }
        });
        data.append('pinataMetadata', metadata);
    
        //pinataOptions are optional
        const pinataOptions = JSON.stringify({
            cidVersion: 0,
            customPinPolicy: {
                regions: [
                    {
                        id: 'FRA1',
                        desiredReplicationCount: 1
                    },
                    {
                        id: 'NYC1',
                        desiredReplicationCount: 2
                    }
                ]
            }
        });
        data.append('pinataOptions', pinataOptions);
    
        return axios 
            .post(url, data, {
                maxBodyLength: 'Infinity',
                headers: {
                    'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
                    pinata_api_key: key,
                    pinata_secret_api_key: secret,
                }
            })
            .then(function (response) {
                console.log("image uploaded", response.data.IpfsHash)
                return {
                   success: true,
                   pinataURL: "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash
               };
            })
            .catch(function (error) {
                console.log(error)
                return {
                    success: false,
                    message: error.message,
                }
    
        });
    };
    

将前端与智能合约集成
----------

将SellNFT.js文件代码进行替换

![](https://storage.googleapis.com/papyrus_images/393fee3b9daa8a12ec57d3468b2a7a010039ab5f2a8b7ff857e5dcc368965be0.png)

替换代码：记得保存

    import Navbar from "./Navbar";
    import { useState } from "react";
    import { uploadFileToIPFS, uploadJSONToIPFS } from "../pinata";
    import Marketplace from '../Marketplace.json';
    import { useLocation } from "react-router";
    
    export default function SellNFT () {
        const [formParams, updateFormParams] = useState({ name: '', description: '', price: ''});
        const [fileURL, setFileURL] = useState(null);
        const ethers = require("ethers");
        const [message, updateMessage] = useState('');
        const location = useLocation();
    
        //This function uploads the NFT image to IPFS
        async function OnChangeFile(e) {
            var file = e.target.files[0];
            //check for file extension
            try {
                //upload the file to IPFS
                const response = await uploadFileToIPFS(file);
                if(response.success === true) {
                    console.log("Uploaded image to Pinata: ", response.pinataURL)
                    setFileURL(response.pinataURL);
                }
            }
            catch(e) {
                console.log("Error during file upload", e);
            }
        }
    
        //This function uploads the metadata to IPDS
        async function uploadMetadataToIPFS() {
            const {name, description, price} = formParams;
            //Make sure that none of the fields are empty
            if( !name || !description || !price || !fileURL)
                return;
    
            const nftJSON = {
                name, description, price, image: fileURL
            }
    
            try {
                //upload the metadata JSON to IPFS
                const response = await uploadJSONToIPFS(nftJSON);
                if(response.success === true){
                    console.log("Uploaded JSON to Pinata: ", response)
                    return response.pinataURL;
                }
            }
            catch(e) {
                console.log("error uploading JSON metadata:", e)
            }
        }
    
        async function listNFT(e) {
            e.preventDefault();
    
            //Upload data to IPFS
            try {
                const metadataURL = await uploadMetadataToIPFS();
                //After adding your Hardhat network to your metamask, this code will get providers and signers
                const provider = new ethers.providers.Web3Provider(window.ethereum);
                const signer = provider.getSigner();
                updateMessage("Please wait.. uploading (upto 5 mins)")
    
                //Pull the deployed contract instance
                let contract = new ethers.Contract(Marketplace.address, Marketplace.abi, signer)
    
                //massage the params to be sent to the create NFT request
                const price = ethers.utils.parseUnits(formParams.price, 'ether')
                let listingPrice = await contract.getListPrice()
                listingPrice = listingPrice.toString()
    
                //actually create the NFT
                let transaction = await contract.createToken(metadataURL, price, { value: listingPrice })
                await transaction.wait()
    
                alert("Successfully listed your NFT!");
                updateMessage("");
                updateFormParams({ name: '', description: '', price: ''});
                window.location.replace("/")
            }
            catch(e) {
                alert( "Upload error"+e )
            }
        }
    
        console.log("Working", process.env);
        return (
            <div className="">
            <Navbar></Navbar>
            <div className="flex flex-col place-items-center mt-10" id="nftForm">
                <form className="bg-white shadow-md rounded px-8 pt-4 pb-8 mb-4">
                <h3 className="text-center font-bold text-purple-500 mb-8">Upload your NFT to the marketplace</h3>
                    <div className="mb-4">
                        <label className="block text-purple-500 text-sm font-bold mb-2" htmlFor="name">NFT Name</label>
                        <input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="name" type="text" placeholder="Axie#4563" onChange={e => updateFormParams({...formParams, name: e.target.value})} value={formParams.name}></input>
                    </div>
                    <div className="mb-6">
                        <label className="block text-purple-500 text-sm font-bold mb-2" htmlFor="description">NFT Description</label>
                        <textarea className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" cols="40" rows="5" id="description" type="text" placeholder="Axie Infinity Collection" value={formParams.description} onChange={e => updateFormParams({...formParams, description: e.target.value})}></textarea>
                    </div>
                    <div className="mb-6">
                        <label className="block text-purple-500 text-sm font-bold mb-2" htmlFor="price">Price (in ETH)</label>
                        <input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" type="number" placeholder="Min 0.01 ETH" step="0.01" value={formParams.price} onChange={e => updateFormParams({...formParams, price: e.target.value})}></input>
                    </div>
                    <div>
                        <label className="block text-purple-500 text-sm font-bold mb-2" htmlFor="image">Upload Image</label>
                        <input type={"file"} onChange={OnChangeFile}></input>
                    </div>
                    <br></br>
                    <div className="text-green text-center">{message}</div>
                    <button onClick={listNFT} className="font-bold mt-10 w-full bg-purple-500 text-white rounded p-2 shadow-lg">
                        List NFT
                    </button>
                </form>
            </div>
            </div>
        )
    }
    

将Marketplace.js代码进行替换

![](https://storage.googleapis.com/papyrus_images/236d8a5c9c009d784b49155304b4117b6bd7849ce361aa332e07fd95d8266fd2.png)

替换代码记得保存

    import Navbar from "./Navbar";
    import NFTTile from "./NFTTile";
    import MarketplaceJSON from "../Marketplace.json";
    import axios from "axios";
    import { useState } from "react";
    
    export default function Marketplace() {
    const sampleData = [
        {
            "name": "NFT#1",
            "description": "Alchemy's First NFT",
            "website":"http://axieinfinity.io",
            "image":"https://gateway.pinata.cloud/ipfs/QmTsRJX7r5gyubjkdmzFrKQhHv74p5wT9LdeF1m3RTqrE5",
            "price":"0.03ETH",
            "currentlySelling":"True",
            "address":"0xe81Bf5A757CB4f7F82a2F23b1e59bE45c33c5b13",
        },
        {
            "name": "NFT#2",
            "description": "Alchemy's Second NFT",
            "website":"http://axieinfinity.io",
            "image":"https://gateway.pinata.cloud/ipfs/QmdhoL9K8my2vi3fej97foiqGmJ389SMs55oC5EdkrxF2M",
            "price":"0.03ETH",
            "currentlySelling":"True",
            "address":"0xe81Bf5A757C4f7F82a2F23b1e59bE45c33c5b13",
        },
        {
            "name": "NFT#3",
            "description": "Alchemy's Third NFT",
            "website":"http://axieinfinity.io",
            "image":"https://gateway.pinata.cloud/ipfs/QmTsRJX7r5gyubjkdmzFrKQhHv74p5wT9LdeF1m3RTqrE5",
            "price":"0.03ETH",
            "currentlySelling":"True",
            "address":"0xe81Bf5A757C4f7F82a2F23b1e59bE45c33c5b13",
        },
    ];
    const [data, updateData] = useState(sampleData);
    const [dataFetched, updateFetched] = useState(false);
    
    async function getAllNFTs() {
        const ethers = require("ethers");
        //After adding your Hardhat network to your metamask, this code will get providers and signers
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const signer = provider.getSigner();
        //Pull the deployed contract instance
        let contract = new ethers.Contract(MarketplaceJSON.address, MarketplaceJSON.abi, signer)
        //create an NFT Token
        let transaction = await contract.getAllNFTs()
    
        //Fetch all the details of every NFT from the contract and display
        const items = await Promise.all(transaction.map(async i => {
            const tokenURI = await contract.tokenURI(i.tokenId);
            let meta = await axios.get(tokenURI);
            meta = meta.data;
    
            let price = ethers.utils.formatUnits(i.price.toString(), 'ether');
            let item = {
                price,
                tokenId: i.tokenId.toNumber(),
                seller: i.seller,
                owner: i.owner,
                image: meta.image,
                name: meta.name,
                description: meta.description,
            }
            return item;
        }))
    
        updateFetched(true);
        updateData(items);
    }
    
    if(!dataFetched)
        getAllNFTs();
    
    return (
        <div>
            <Navbar></Navbar>
            <div className="flex flex-col place-items-center mt-20">
                <div className="md:text-xl font-bold text-white">
                    Top NFTs
                </div>
                <div className="flex mt-5 justify-between flex-wrap max-w-screen-xl text-center">
                    {data.map((value, index) => {
                        return <NFTTile data={value} key={index}></NFTTile>;
                    })}
                </div>
            </div>            
        </div>
    );
    
    }
    

将Profile.js文件代码替换

![](https://storage.googleapis.com/papyrus_images/f70bd7f6343e5bb1381aee587c14c883cd017277f562dbbe157fd030be7127cd.png)

替换代码记得保存

    import Navbar from "./Navbar";
    import { useLocation, useParams } from 'react-router-dom';
    import MarketplaceJSON from "../Marketplace.json";
    import axios from "axios";
    import { useState } from "react";
    import NFTTile from "./NFTTile";
    
    export default function Profile () {
        const [data, updateData] = useState([]);
        const [address, updateAddress] = useState("0x");
        const [totalPrice, updateTotalPrice] = useState("0");
        const [dataFetched, updateFetched] = useState(false);
    
        async function getNFTData(tokenId) {
            const ethers = require("ethers");
            let sumPrice = 0;
    
            //After adding your Hardhat network to your metamask, this code will get providers and signers
            const provider = new ethers.providers.Web3Provider(window.ethereum);
            await provider.send('eth_requestAccounts', []);
            const signer = provider.getSigner();
            const addr = await signer.getAddress();
    
            //Pull the deployed contract instance
            let contract = new ethers.Contract(MarketplaceJSON.address, MarketplaceJSON.abi, signer)
    
            //create an NFT Token
            let transaction = await contract.getMyNFTs()
    
            /*
            * Below function takes the metadata from tokenURI and the data returned by getMyNFTs() contract function
            * and creates an object of information that is to be displayed
            */
            
            const items = await Promise.all(transaction.map(async i => {
                const tokenURI = await contract.tokenURI(i.tokenId);
                let meta = await axios.get(tokenURI);
                meta = meta.data;
    
                let price = ethers.utils.formatUnits(i.price.toString(), 'ether');
                let item = {
                    price,
                    tokenId: i.tokenId.toNumber(),
                    seller: i.seller,
                    owner: i.owner,
                    image: meta.image,
                    name: meta.name,
                    description: meta.description,
                }
                sumPrice += Number(price);
                return item;
            }))
    
            updateData(items);
            updateFetched(true);
            updateAddress(addr);
            updateTotalPrice(sumPrice.toPrecision(3));
        }
    
        const params = useParams();
        const tokenId = params.tokenId;
        if(!dataFetched)
            getNFTData(tokenId);
        
        return (
            <div className="profileClass" style={{"min-height":"100vh"}}>
                <Navbar></Navbar>
                <div className="profileClass">
                <div className="flex text-center flex-col mt-11 md:text-2xl text-white">
                    <div className="mb-5">
                        <h2 className="font-bold">Wallet Address</h2>  
                        {address}
                    </div>
                </div>
                <div className="flex flex-row text-center justify-center mt-10 md:text-2xl text-white">
                        <div>
                            <h2 className="font-bold">No. of NFTs</h2>
                            {data.length}
                        </div>
                        <div className="ml-20">
                            <h2 className="font-bold">Total Value</h2>
                            {totalPrice} ETH
                        </div>
                </div>
                <div className="flex flex-col text-center items-center mt-11 text-white">
                    <h2 className="font-bold">Your NFTs</h2>
                    <div className="flex justify-center flex-wrap max-w-screen-xl">
                        {data.map((value, index) => {
                        return <NFTTile data={value} key={index}></NFTTile>;
                        })}
                    </div>
                    <div className="mt-10 text-xl">
                        {data.length == 0 ? "Oops, No NFT data to display (Are you logged in?)":""}
                    </div>
                </div>
                </div>
            </div>
        )
    };
    

将NFTPage.js文件代码替换

![](https://storage.googleapis.com/papyrus_images/dd4f498972652a190ac23cf05f42ef31debe9fee14092f9a12cfd54925da4338.png)

替换代码记得保存

    import Navbar from "./Navbar";
    import axie from "../tile.jpeg";
    import { useLocation, useParams } from 'react-router-dom';
    import MarketplaceJSON from "../Marketplace.json";
    import axios from "axios";
    import { useState } from "react";
    
    export default function NFTPage (props) {
    
      const [data, updateData] = useState({});
      const [dataFetched, updateDataFetched] = useState(false);
      const [message, updateMessage] = useState("");
      const [currAddress, updateCurrAddress] = useState("0x");
    
      async function getNFTData(tokenId) {
        const ethers = require("ethers");
        //After adding your Hardhat network to your metamask, this code will get providers and signers
        const provider = new ethers.providers.Web3Provider(window.ethereum);
        const signer = provider.getSigner();
        const addr = await signer.getAddress();
        //Pull the deployed contract instance
        let contract = new ethers.Contract(MarketplaceJSON.address,         MarketplaceJSON.abi, signer)
        //create an NFT Token
        const tokenURI = await contract.tokenURI(tokenId);
        const listedToken = await contract.getListedTokenForId(tokenId);
        let meta = await axios.get(tokenURI);
        meta = meta.data;
        console.log(listedToken);
    
          let item = {
          price: meta.price,
          tokenId: tokenId,
          seller: listedToken.seller,
          owner: listedToken.owner,
          image: meta.image,
          name: meta.name,
          description: meta.description,
        }
        console.log(item);
        updateData(item);
        updateDataFetched(true);
        console.log("address", addr)
        updateCurrAddress(addr);
      }
    
      async function buyNFT(tokenId) {
        try {
          const ethers = require("ethers");
          //After adding your Hardhat network to your metamask, this code will get providers and signers
          const provider = new ethers.providers.Web3Provider(window.ethereum);
          const signer = provider.getSigner();
    
          //Pull the deployed contract instance
          let contract = new ethers.Contract(MarketplaceJSON.address,       MarketplaceJSON.abi, signer);
          const salePrice = ethers.utils.parseUnits(data.price, 'ether')
          updateMessage("Buying the NFT... Please Wait (Upto 5 mins)")
          //run the executeSale function
          let transaction = await contract.executeSale(tokenId,     {value:salePrice});
          await transaction.wait();
    
          alert('You successfully bought the NFT!');
          updateMessage("");
        }
        catch(e) {
        alert("Upload Error"+e)
      }
    }
    
    const params = useParams();
    const tokenId = params.tokenId;
    if(!dataFetched)
    getNFTData(tokenId);
    
    return(
    <div style={{"min-height":"100vh"}}>
    <Navbar></Navbar>
    <div className="flex ml-20 mt-20">
    <img src={data.image} alt="" className="w-2/5" />
    <div className="text-xl ml-20 space-y-8 text-white shadow-2xl rounded-lg border-2 p-5">
    <div>
    Name: {data.name}
    </div>
    <div>
    Description: {data.description}
    </div>
    <div>
    Price: <span className="">{data.price + " ETH"}</span>
    </div>
    <div>
    Owner: <span className="text-sm">{data.owner}</span>
    </div>
    <div>
    Seller: <span className="text-sm">{data.seller}</span>
    </div>
    <div>
    { currAddress == data.owner || currAddress == data.seller ?
    <button className="enableEthereumButton bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-sm" onClick={() => buyNFT(tokenId)}>Buy this NFT</button>
    : <div className="text-emerald-700">You are the owner of this NFT</div>
    }
    
    <div className="text-green text-center mt-3">{message}</div>
    </div>
    </div>
    </div>
    </div>
    )
    }
    

将navbar.js代码替换

![](https://storage.googleapis.com/papyrus_images/35c294ccb4e3021924afefcf14b017172dbff4591a196dd9da03b979ae397500.png)

替换代码记得保存

    import logo from '../logo_3.png';
    import fullLogo from '../full_logo.png';
    import {
      BrowserRouter as Router,
      Switch,
      Route,
      Link,
      useRouteMatch,
      useParams
    } from "react-router-dom";
    import { useEffect, useState } from 'react';
    import { useLocation } from 'react-router';
    
    function Navbar() {
    
    const [connected, toggleConnect] = useState(false);
    const location = useLocation();
    const [currAddress, updateAddress] = useState('0x');
    
    async function getAddress() {
      const ethers = require("ethers");
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const addr = await signer.getAddress();
      updateAddress(addr);
    }
    
    function updateButton() {
      const ethereumButton = document.querySelector('.enableEthereumButton');
      ethereumButton.textContent = "Connected";
      ethereumButton.classList.remove("hover:bg-blue-70");
      ethereumButton.classList.remove("bg-blue-500");
      ethereumButton.classList.add("hover:bg-green-70");
      ethereumButton.classList.add("bg-green-500");
    }
    
    async function connectWebsite() {
    
        const chainId = await window.ethereum.request({ method: 'eth_chainId' });
        if(chainId !== '0x5')
        {
          //alert('Incorrect network! Switch your metamask network to Rinkeby');
          await window.ethereum.request({
            method: 'wallet_switchEthereumChain',
            params: [{ chainId: '0x5' }],
         })
        }  
        await window.ethereum.request({ method: 'eth_requestAccounts' })
          .then(() => {
            updateButton();
            console.log("here");
            getAddress();
            window.location.replace(location.pathname)
          });
    }
    
      useEffect(() => {
        let val = window.ethereum.isConnected();
        if(val)
        {
          console.log("here");
          getAddress();
          toggleConnect(val);
          updateButton();
        }
    
        window.ethereum.on('accountsChanged', function(accounts){
          window.location.replace(location.pathname)
        })
      });
    
        return (
          <div className="">
            <nav className="w-screen">
              <ul className='flex items-end justify-between py-3 bg-transparent text-white pr-5'>
              <li className='flex items-end ml-5 pb-2'>
                <Link to="/">
                <img src={fullLogo} alt="" width={120} height={120} className="inline-block -mt-2"/>
                <div className='inline-block font-bold text-xl ml-2'>
                  NFT Marketplace
                </div>
                </Link>
              </li>
              <li className='w-2/6'>
                <ul className='lg:flex justify-between font-bold mr-10 text-lg'>
                  {location.pathname === "/" ? 
                  <li className='border-b-2 hover:pb-0 p-2'>
                    <Link to="/">Marketplace</Link>
                  </li>
                  :
                  <li className='hover:border-b-2 hover:pb-0 p-2'>
                    <Link to="/">Marketplace</Link>
                  </li>              
                  }
                  {location.pathname === "/sellNFT" ? 
                  <li className='border-b-2 hover:pb-0 p-2'>
                    <Link to="/sellNFT">List My NFT</Link>
                  </li>
                  :
                  <li className='hover:border-b-2 hover:pb-0 p-2'>
                    <Link to="/sellNFT">List My NFT</Link>
                  </li>              
                  }              
                  {location.pathname === "/profile" ? 
                  <li className='border-b-2 hover:pb-0 p-2'>
                    <Link to="/profile">Profile</Link>
                  </li>
                  :
                  <li className='hover:border-b-2 hover:pb-0 p-2'>
                    <Link to="/profile">Profile</Link>
                  </li>              
                  }  
                  <li>
                    <button className="enableEthereumButton bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded text-sm" onClick={connectWebsite}>{connected? "Connected":"Connect Wallet"}</button>
                  </li>
                </ul>
              </li>
              </ul>
            </nav>
            <div className='text-white text-bold text-right mr-10 text-sm'>
              {currAddress !== "0x" ? "Connected to":"Not Connected. Please login to view NFTs"} {currAddress !== "0x" ? (currAddress.substring(0,15)+'...'):""}
            </div>
          </div>
        );
      }
    
      export default Navbar;
    

测试
--

项目输入npm start，启动项目，连接钱包，选择list my nft目录

![](https://storage.googleapis.com/papyrus_images/f21bf2c63055573441aab029d7c29587a604fcb37c4101a08030ebb4a325248b.png)

下图输入Y

![](https://storage.googleapis.com/papyrus_images/7f04932b893246170930efb3da8b5b060552857484ec7a113eeabf88c10ac313.png)

如果下图弹出的网页不能连接钱包或者显示异常把之前的终端关闭然后在重复测试的步骤，如果还显示异常不能连接钱包请检查替换的代码

![](https://storage.googleapis.com/papyrus_images/23327346d5d2b23fe750473932bb8b55c4389fc37474274b9d6304b1cab8002e.png)

---

*Originally published on [xiaoge](https://paragraph.com/@xiaoge/alchemy-7)*
