2019 年 12 月,Alchemy 完成1500 万美元 A 轮融资,资方为 Pantera Capital,斯坦福大学,Coinbase,三星等。
2021 年 4 月,Alchemy 以 5.05 亿美元估值完成8000 万美元 B 轮融资,Coatue 和 Addition 领投,DFJ Growth、K5 Global、Chainsmokers(烟鬼组合)、演员 Jared Leto 和 Glazer 家族参投。
2021 年 10 月,Alchemy 以 35 亿美元估值完成2.5 亿美元 C 轮融资,由 a16z 领投的。
2022 年 2 月,Alchemy 以 102 亿美元估值完成2 亿美元融资,Lightspeed 与 Silver Lake 领投。
Alchemy 是一个背景强大、经费充足、踏实做事、没有发币的团队,这样的项目不刷,难道去刷土狗吗?
并且,Alchemy 计划将新资金用于推广 Web3 采用,这方面的一些举措包括推出 Web3 University,就是现在的 Road to Web3 活动,活动为期 10 周,每周一个 NFT。看了下 nft 数量极少,估计由于任务难度大,很多小伙伴直接放弃,这样的项目若是空投,绝对是大毛。
1.注册一个 Alchemy帐户。进入 dashboard,点击 create app。注册一个 Alchemy帐户。进入 dashboard,点击 create app。
然后如下设置,点击 create app。
点击 view key
复制 HTTPS 的框内内容。
1. 将 MetaMask 连接到 Goerli 网络 ,然后 使用 Goerli 水龙头请求 Goerli ETH 。 您将需要 Goerli ETH 来部署智能合约并将 NFT 上传到您的 NFT 市场。
2.点击添加 Goerli 网络,如图填写(其中 RPC URL 为 step1 获取的链接),点击保存即可。
1.为方便起见,官方已将基本代码上传到 GitHub 存储库。 此代码已编写所有前端,但没有智能合约或与前端的集成。要克隆存储库,请在控制台一步一步运行以下命令:
git clone https://github.com/alchemyplatform/RTW3-Week7-NFT-Marketplace.gitcd RTW3-Week7-NFT-Marketplacenpm installnpm start
2.运行上面四个指令完毕之后,会跳出这个。
(下面这个图由于我的 3000 端口还在用,所以是 3001 端口,大家的估计还是 3000 啊)
1.打开 vscode,找到您刚才创建的文件夹,点击选择文件夹。
2.再打开一个控制台,cd 进入 RTW3-Week7-NFT-Marketplace 文件夹,然后输入 echo test>.env 创建一个.env 文件。
3.回到 vscode,可以看到 env 文件,在其中粘贴如下代码
REACT_APP_ALCHEMY_API_URL="<YOUR_API_URL>"(这个是 step1 中最后得到的 HTTPS)REACT_APP_PRIVATE_KEY="<YOUR_PRIVATE_KEY>" ( 这个是您将用于开发的小狐狸钱包的私钥 )
复制这个替换第一行等号后的内容。
复制私钥替换第二行等号后的内容
4.如果尚未安装,请在控制台输入 npm install dotenv –save
5.在您的主目录中,确保将以下代码添加到您的 hardhat.config.js
文件:(其实就改了 env 相关的两行代码)
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 } } }};
1.进入pinata 官网 ,选择顶部的“new key”按钮,如图设置,点击 create key。
2.然后,您将看到一个包含您的 API 信息的弹出窗口。 将其复制到安全的地方。
3.回到 vscode,点开.env 文件,在后面加入如下代码。
REACT_APP_PINATA_KEY="<YOUR_PINATA_KEY>"REACT_APP_PINATA_SECRET="<YOUR_PINATA_SECRET>"
第一行第二行分别填图示中内容。
填完后如图所示:
1.在 vscode 中找到 NFTMarketplace.sol 文件(在 contracts 下面),将其中的代码更换为下面的,注意保存。官方有对每一步的解释,可以了解下。
//SPDX-License-Identifier: Unlicensepragma solidity ^0.8.0;import "hardhat/console.sol";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; //_tokenIds variable has the most recent minted tokenId Counters.Counter private _tokenIds; //Keeps track of the number of items sold on the marketplace Counters.Counter private _itemsSold; //owner is the contract address that created the smart contract address payable owner; //The fee charged by the marketplace to be allowed to list an NFT uint256 listPrice = 0.01 ether; //The structure to store info about a listed token struct ListedToken { uint256 tokenId; address payable owner; address payable seller; uint256 price; bool currentlyListed; } //the event emitted when a token is successfully listed event TokenListedSuccess ( uint256 indexed tokenId, address owner, address seller, uint256 price, bool currentlyListed ); //This mapping maps tokenId to token info and is helpful when retrieving details about a tokenId mapping(uint256 => ListedToken) private idToListedToken; constructor() ERC721("NFTMarketplace", "NFTM") { owner = payable(msg.sender); } 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(); } //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 ); } //This will return all the NFTs currently listed to be sold on the marketplace function getAllNFTs() public view returns (ListedToken[] memory) { uint nftCount = _tokenIds.current(); ListedToken[] memory tokens = new ListedToken[](nftCount); 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; } //Returns all the NFTs that the current user is owner or seller in 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 ListedToken[](itemCount); 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; } 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); } //We might add a resell token function in the future //In that case, tokens won't be listed by default but users can send a request to actually list a token //Currently NFTs are listed by default}
1.还是在 vscode 中,在 scripts 下面找到一个名为 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); });
2.然后打开控制台并执行以下命令:npx hardhat run –network goerli scripts/deploy.js
1.在 src 中,在名为 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, } });};
1.将以下代码添加到您的 src/components/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> )}
2.将下面的代码粘贴到 src/components/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>);}
3.将下面的代码粘贴到 src/components/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 [dataFetched, updateFetched] = useState(false); const [address, updateAddress] = useState("0x"); const [totalPrice, updateTotalPrice] = useState("0"); 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); 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> )};
4.将下面的代码粘贴到 src/components/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> )}
上述步骤记得保存。
1.在控制台输入 npm start,可以看到成功了。可以试一下上传 nft 和购买 nft 功能,也可以直接提交。
可以提交项目 github,具体方式见第六周教程最后
也可以提交部署的合约地址,在这里
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。