# Alchemy第七周教程 **Published by:** [xiaoge](https://paragraph.com/@xiaoge/) **Published on:** 2022-11-12 **URL:** https://paragraph.com/@xiaoge/alchemy-7 ## Content 安装VScode和其它一些软件我就不写攻略了.先决条件:用之前注册好的Alchemy账号并创建一个新的应用程序因为之前已经创建好了,所以直接用就好,没创建的点击右上角的CREATE APP进行创建。设置MetaMask钱包以进行开发在小狐狸上点击添加网络,然后填入对应参数 Network Name: Goerli Test Network RPC base URL: https://eth-goerli.alchemyapi.io/v2/你创建应用的API KEY Chain ID: 5 Block Explorer URL: https://goerli.etherscan.io/ Symbol (Optional): ETH 添加完成后导航到下面地址领取测试代币: https://goerlifaucet.com/设置存储库GitHub地址: https://github.com/alchemyplatform/RTW3-Week7-NFT-Marketplace 下载ZIP包,下载完成后解压然后我们用VSCode打开此项目选择刚才解压出来的文件夹 然后在项目中打开终端输入npm install,等待依赖下载完成接着输入npm start ,浏览器就会弹出下面网页设置环境变量和 Hardhat 配置我们新增一个cmd终端在新的终端输入npm install dotenv --save,下载安装 dotenv在根目录创建.env文件找到hardhat.config.js文件,并将其中代码替换替换代码(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 创建key点击创建,然后会弹出一个窗口,将其中的信息复制在.env中添加配置(记得保存)REACT_APP_PINATA_KEY="<YOUR_PINATA_KEY>" #上图的API Key REACT_APP_PINATA_SECRET="<YOUR_PINATA_SECRET>" #上图的API Secret 编写智能合约打开NFTMarketplace.sol文件,替换代码替换代码:替换完记得保存//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文件,替换代码替换代码:记得保存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写入代码里(记得保存不然不生效)我们输入npx hardhat run --network goerli scripts/deploy.js 部署合约,部署完成后我们可以在marketplace.json文件中查看合约地址和ABI。 当你看到生成了一下的文件则说明你已经成功部署了我们在部署的json文件中也可以找到合约的地址,我们去浏览器查看下帝哥部署的合约地址是:0xa8b8DF1472CC853137483DF139e0c08e7C8F5035添加将 NFT 元数据上传到 Piñata 的功能将pinata.js文件中的代码替换替换代码:记得保存//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文件代码进行替换替换代码:记得保存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代码进行替换替换代码记得保存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文件代码替换替换代码记得保存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文件代码替换替换代码记得保存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代码替换替换代码记得保存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目录下图输入Y如果下图弹出的网页不能连接钱包或者显示异常把之前的终端关闭然后在重复测试的步骤,如果还显示异常不能连接钱包请检查替换的代码 ## Publication Information - [xiaoge](https://paragraph.com/@xiaoge/): Publication homepage - [All Posts](https://paragraph.com/@xiaoge/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@xiaoge): Subscribe to updates - [Twitter](https://twitter.com/Chrish9010): Follow on Twitter