推特:Greta008
Buildspace 是一家 Web3 构建者网络服务商,旨在为用户提供探索职业生涯发展的学习平台,现在正专注于 Web3 领域。
11月18号完成 1000 万美元天使轮融资,投资方为「a16z」「Founders Inc」「Weekend Fund」「YC」「Vayner Fund」「Protocol Labs」「Orange DAO」「Solana Ventures」「OpenSea Ventures」「Alchemy Ventures」「Dreamers VC」。
全是熟悉的顶级大投资机构和项目方啊,Buildspace 跟Alchemy很类似,都是web3学习平台,虽然前期写了系列Alchemy教程,但我并没有刷1000个号,都是谣传哈。相对于Alchemy,Buildspace发币的可能性还要大很多,Alchemy有法币付费渠道,Buildspace暂未看到任何利益获取渠道,虽然这种平台确实没有发币的先例,谁知道Buildspace会不会是第一个呢?
1.进入项目官网,点击右上角start。
2.下拉到这里,点击进去。
3.点击start this build。
4.用谷歌账号登录。
5.添加日历,链接discord后,点击let’s go。
1.把这个看完,点击提交需求。
2.随便写点什么,点击提交。
3,然后点击next。
1.首先要安装环境,环境安装详见我这篇炼金的教程,确保node版本是16。
2.进入控制台,一次性复制以下代码,按回车,稍等几十秒hardhat安装完即可。
mkdir my-wave-portal
cd my-wave-portal
npm init -y
npm install --save-dev hardhat@latest
3.输入npx hardhat,按回车,就会出现下面那个蓝色大字图。
4.连续按三下回车,出现下图。
5.按照提示输入如下代码。
npm install --save-dev "hardhat@^2.12.2" "@nomicfoundation/hardhat-toolbox@^2.0.0"
6.打开vscode,点击file,点击open folder。
7.找到刚才创建的my-wave-portal文件夹,点击选择文件夹。
8.打开后如图所示。
9.打开 hardhat.config.js
文件,将以下代码复制进去并保存。(不保存没用啊,要点保存)
require("@nomicfoundation/hardhat-toolbox");
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
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);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.17",
};
10.最后,运行 npx hardhat node
,应该打印出一堆如下所示的帐户,总共20个。
11.第一步的测试就结束了。清理一下。输入以下代码。
npx hardhat compile
npx hardhat test
12.在VScode中,把 test
下面的 Lock.js
, scripts
.下面的 deploy.js
,以及 contracts
.下面的 Lock.sol
都删除。
13.前往discord的progress频道,发送证明截图。
14.点击submit requirement,上传刚才第十步的图片即可。
1.回到VScode,在 contracts
下面创建一个名为 WavePortal.sol
的文件。
2.向 WavePortal.sol
中复制粘贴下面代码。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import "hardhat/console.sol";
contract WavePortal {
constructor() {
console.log("Yo yo, I am a contract and I am smart");
}
}
3.如果2中你的代码是白色的而不是彩色的,需要安装一下这个插件,看起来舒服些,能显示语法还能报错。
4.还是将2中的截图在discord的progress频道上传,并点击这里的submit requirement上传即可。
1.在 scripts
下面创建一个名为 run.js
的文件。
2.在run.js
的文件中粘贴如下代码,记得保存,如图所示。
const main = async () => {
const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
const waveContract = await waveContractFactory.deploy();
await waveContract.deployed();
console.log("Contract deployed to:", waveContract.address);
};
const runMain = async () => {
try {
await main();
process.exit(0); // exit Node process without error
} catch (error) {
console.log(error);
process.exit(1); // exit Node process while indicating 'Uncaught Fatal Exception' error
}
// Read more about Node exit ('process.exit(num)') status codes here: https://stackoverflow.com/a/47163396/7974948
};
runMain();
3.回到控制台,输入npx hardhat run scripts/run.js ,结果如图所示。
4.继续在discord和网页上传刚才3的截屏。
5.这一步他会跳出来个发推。那就发一下呗。
1.将以下代码粘贴进waveportal.sol 并保存。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import "hardhat/console.sol";
contract WavePortal {
uint256 totalWaves;
constructor() {
console.log("Yo yo, I am a contract and I am smart");
}
function wave() public {
totalWaves += 1;
console.log("%s has waved!", msg.sender);
}
function getTotalWaves() public view returns (uint256) {
console.log("We have %d total waves!", totalWaves);
return totalWaves;
}
}
2.将以下代码粘贴到run.js并保存。
const main = async () => {
const [owner, randomPerson] = await hre.ethers.getSigners();
const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
const waveContract = await waveContractFactory.deploy();
await waveContract.deployed();
console.log("Contract deployed to:", waveContract.address);
console.log("Contract deployed by:", owner.address);
await waveContract.getTotalWaves();
const waveTxn = await waveContract.wave();
await waveTxn.wait();
await waveContract.getTotalWaves();
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
3.回到控制台,输入npx hardhat run scripts/run.js ,得到如下图结果。
4.如果要测试其他用户,还可以添加几行代码。通过VScode在run.js中复制粘贴这个,再回到控制台输入npx hardhat run scripts/run.js试着运行下,就可以得到如图所示结果。
const main = async () => {
const [owner, randomPerson] = await hre.ethers.getSigners();
const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
const waveContract = await waveContractFactory.deploy();
await waveContract.deployed();
console.log("Contract deployed to:", waveContract.address);
console.log("Contract deployed by:", owner.address);
await waveContract.getTotalWaves();
const firstWaveTxn = await waveContract.wave();
await firstWaveTxn.wait();
await waveContract.getTotalWaves();
const secondWaveTxn = await waveContract.connect(randomPerson).wave();
await secondWaveTxn.wait();
await waveContract.getTotalWaves();
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
5.然后继续点击submit requirement,这次要写一个最喜欢的事。
1.回到控制台,输入npx hardhat node,可以看到刚才的20个地址,每个里面有10000eth。
2.在 scripts
下面创建一个名为 deploy.js
的文件,并在其中粘贴如下代码。
const main = async () => {
const [deployer] = await hre.ethers.getSigners();
const accountBalance = await deployer.getBalance();
console.log("Deploying contracts with account: ", deployer.address);
console.log("Account balance: ", accountBalance.toString());
const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
const waveContract = await waveContractFactory.deploy();
await waveContract.deployed();
console.log("WavePortal address: ", waveContract.address);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
3.重新打开一个控制台,输入cd my-wave-portal 进入对应文件夹,再输入以下代码并运行,如图所示就对了。
npx hardhat run scripts/deploy.js --network localhost
4.最后可以到这里校对一下三个文件的代码,不对的话就重新复制粘贴下备用。
5.在dis和网页提交第三步的图片。
1.这里我们选用Replit ,先去注册一个账号。之前跟我做过alchemy的应该都有。
2.官方已经创建了一个React 项目, 只需转到 此处 ,在右侧附近您会看到“Fork Repl”按钮,再点击跳出框的Fork Repl即可。
3.点击顶部的Run,等2分钟就可以了。
4.继续通过dis和网页提交,这一次网页要提交的是网址,就是上一步run之后,右上角那个网址。
1.在 此处 使用 QuickNode 创建一个帐户,邮箱要验证下。
2.点击create an endpoint。
3.选择ETH的Goerli网络。
4.什么都不要选,直接点继续。
5.选免费的,点击create。
6.可以获取两个链接,待会儿有用。
7.领水,以下三个链接都可以用,自取。
8.为了安全起见,回到控制台,输入npm install --save dotenv 。
9.回到VScode,将hardhat.config.js代码更改为下面代码。
require("@nomicfoundation/hardhat-toolbox");
// Import and configure dotenv
require("dotenv").config();
module.exports = {
solidity: "0.8.17",
networks: {
goerli: {
// This value will be replaced on runtime
url: process.env.STAGING_QUICKNODE_KEY,
accounts: [process.env.PRIVATE_KEY],
},
mainnet: {
url: process.env.PROD_QUICKNODE_KEY,
accounts: [process.env.PRIVATE_KEY],
},
},
};
10.在控制台输入echo test>.env 创建一个.env文件。
11.然后输入下面代码。
STAGING_QUICKNODE_KEY=REPLACE_WITH_ACTUAL_QUICKNODE_URL // Goerli Quicknode
PROD_QUICKNODE_KEY=BLAHBLAH // Mainnet Quicknode
PRIVATE_KEY=BLAHBLAH
12.接下来,从 QuickNode 仪表板获取您的 API URL 并将其粘贴第9步代码对应位置(url)。然后,将您的 私人 Goerli 密钥(而不是您的公共地址!)也粘贴到对应位置(accounts)。
13.回到控制台运行 npx hardhat run scripts/deploy.js --network goerli 。如图所示即可。复制最后一行已部署合约的地址并将其保存在某处。 别弄丢了! 稍后您将需要它作为前端:)。
14.您可以到这个网址查看刚刚部署的合约。
15.这一步没让去dis提交,就直接在网页提交合约地址即可。然后再分享个推特。
1.前往 Replit 并前往 App.jsx
下面的 src
,这将是我们完成所有工作的地方。
2.将以下代码粘贴进去,替换掉原来的代码。
import React, { useEffect, useState } from "react";
import "./App.css";
const getEthereumObject = () => window.ethereum;
/*
* This function returns the first linked account found.
* If there is no account linked, it will return null.
*/
const findMetaMaskAccount = async () => {
try {
const ethereum = getEthereumObject();
/*
* First make sure we have access to the Ethereum object.
*/
if (!ethereum) {
console.error("Make sure you have Metamask!");
return null;
}
console.log("We have the Ethereum object", ethereum);
const accounts = await ethereum.request({ method: "eth_accounts" });
if (accounts.length !== 0) {
const account = accounts[0];
console.log("Found an authorized account:", account);
return account;
} else {
console.error("No authorized account found");
return null;
}
} catch (error) {
console.error(error);
return null;
}
};
const App = () => {
const [currentAccount, setCurrentAccount] = useState("");
const connectWallet = async () => {
try {
const ethereum = getEthereumObject();
if (!ethereum) {
alert("Get MetaMask!");
return;
}
const accounts = await ethereum.request({
method: "eth_requestAccounts",
});
console.log("Connected", accounts[0]);
setCurrentAccount(accounts[0]);
} catch (error) {
console.error(error);
}
};
/*
* This runs our function when the page loads.
* More technically, when the App component "mounts".
*/
useEffect(async () => {
const account = await findMetaMaskAccount();
if (account !== null) {
setCurrentAccount(account);
}
}, []);
return (
<div className="mainContainer">
<div className="dataContainer">
<div className="header">
👋 Hey there!
</div>
<div className="bio">
I am Farza and I worked on self-driving cars so that's pretty cool
right? Connect your Ethereum wallet and wave at me!
</div>
<button className="waveButton" onClick={null}>
Wave at Me
</button>
{/*
* If there is no currentAccount render this button
*/}
{!currentAccount && (
<button className="waveButton" onClick={connectWallet}>
Connect Wallet
</button>
)}
</div>
</div>
);
};
export default App;
3.点击Run。
4.把那个网址复制出来打开,你就可以看到这个界面,测试一下链接钱包功能即可。
5.这一次提交他问你和他人一起学习的感觉,回答下即可。
1.将以下代码复制粘贴进App.jsx,const contractAddress那一行要改成你自己之前生成的合约地址(之前跟你说前端有用的那个)。报错不要管,继续往下走,是个文件缺了,下面会补上。
import React, { useEffect, useState } from "react";
import { ethers } from "ethers";
import './App.css';
import abi from './utils/WavePortal.json';
const App = () => {
const [currentAccount, setCurrentAccount] = useState("");
/**
* Create a varaible here that holds the contract address after you deploy!
*/
const contractAddress = "0xd5f08a0ae197482FA808cE84E00E97d940dBD26E";
const contractABI = abi.abi;
const checkIfWalletIsConnected = async () => {
try {
const { ethereum } = window;
if (!ethereum) {
console.log("Make sure you have metamask!");
return;
} else {
console.log("We have the ethereum object", ethereum);
}
const accounts = await ethereum.request({ method: 'eth_accounts' });
if (accounts.length !== 0) {
const account = accounts[0];
console.log("Found an authorized account:", account);
setCurrentAccount(account)
} else {
console.log("No authorized account found")
}
} catch (error) {
console.log(error);
}
}
const connectWallet = async () => {
try {
const { ethereum } = window;
if (!ethereum) {
alert("Get MetaMask!");
return;
}
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
console.log("Connected", accounts[0]);
setCurrentAccount(accounts[0]);
} catch (error) {
console.log(error)
}
}
const wave = async () => {
try {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);
let count = await wavePortalContract.getTotalWaves();
console.log("Retrieved total wave count...", count.toNumber());
const waveTxn = await wavePortalContract.wave();
console.log("Mining...", waveTxn.hash);
await waveTxn.wait();
console.log("Mined -- ", waveTxn.hash);
count = await wavePortalContract.getTotalWaves();
console.log("Retrieved total wave count...", count.toNumber());
} else {
console.log("Ethereum object doesn't exist!");
}
} catch (error) {
console.log(error)
}
}
useEffect(() => {
checkIfWalletIsConnected();
}, [])
return (
<div className="mainContainer">
<div className="dataContainer">
<div className="header">
👋 Hey there!
</div>
<div className="bio">
I am farza and I worked on self-driving cars so that's pretty cool right? Connect your Ethereum wallet and wave at me!
</div>
<button className="waveButton" onClick={wave}>
Wave at Me
</button>
{!currentAccount && (
<button className="waveButton" onClick={connectWallet}>
Connect Wallet
</button>
)}
</div>
</div>
);
}
export default App
2.在 src
. 下面创建一个名为 utils
的文件夹, 在 utils
下面再创建一个名为 WavePortal.json
.的空文件。
3.再回到VScode,将如下路径中的代码粘贴进步骤2的空文件中。
artifacts/contracts/WavePortal.sol/WavePortal.json
4.再到网页端试一下,链接小狐狸后,这次点击Wave at me有反应了,在小狐狸中确认即可。
5.然后在replit的CSS那里稍微改一下前端即可。估计是官方怕假提交。
6.将网页截图上传即可。
1.回到VScode,再次修改WavePortal.sol的代码。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import "hardhat/console.sol";
contract WavePortal {
uint256 totalWaves;
/*
* A little magic, Google what events are in Solidity!
*/
event NewWave(address indexed from, uint256 timestamp, string message);
/*
* I created a struct here named Wave.
* A struct is basically a custom datatype where we can customize what we want to hold inside it.
*/
struct Wave {
address waver; // The address of the user who waved.
string message; // The message the user sent.
uint256 timestamp; // The timestamp when the user waved.
}
/*
* I declare a variable waves that lets me store an array of structs.
* This is what lets me hold all the waves anyone ever sends to me!
*/
Wave[] waves;
constructor() {
console.log("I AM SMART CONTRACT. POG.");
}
/*
* You'll notice I changed the wave function a little here as well and
* now it requires a string called _message. This is the message our user
* sends us from the frontend!
*/
function wave(string memory _message) public {
totalWaves += 1;
console.log("%s waved w/ message %s", msg.sender, _message);
/*
* This is where I actually store the wave data in the array.
*/
waves.push(Wave(msg.sender, _message, block.timestamp));
/*
* I added some fanciness here, Google it and try to figure out what it is!
* Let me know what you learn in #general-chill-chat
*/
emit NewWave(msg.sender, block.timestamp, _message);
}
/*
* I added a function getAllWaves which will return the struct array, waves, to us.
* This will make it easy to retrieve the waves from our website!
*/
function getAllWaves() public view returns (Wave[] memory) {
return waves;
}
function getTotalWaves() public view returns (uint256) {
// Optional: Add this line if you want to see the contract print the value!
// We'll also print it over in run.js as well.
console.log("We have %d total waves!", totalWaves);
return totalWaves;
}
}
2.更新 run.js
. 代码如下。
const main = async () => {
const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
const waveContract = await waveContractFactory.deploy();
await waveContract.deployed();
console.log("Contract addy:", waveContract.address);
let waveCount;
waveCount = await waveContract.getTotalWaves();
console.log(waveCount.toNumber());
/**
* Let's send a few waves!
*/
let waveTxn = await waveContract.wave("A message!");
await waveTxn.wait(); // Wait for the transaction to be mined
const [_, randomPerson] = await hre.ethers.getSigners();
waveTxn = await waveContract.connect(randomPerson).wave("Another message!");
await waveTxn.wait(); // Wait for the transaction to be mined
let allWaves = await waveContract.getAllWaves();
console.log(allWaves);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
3.在控制台输入npx hardhat run scripts/run.js
,结果如图所示。
4.需要重新部署一下。再次使用这个代码npx hardhat run scripts/deploy.js --network goerli
5.改变 App.js
中的 contractAddress
地址为新生成的WavePortal地址。
6.重复step10步骤2、3,复制粘贴进去即可。
7.app.jsx中代码换成这个就可以了,合约地址换成你自己之前生成的,感谢AckooLu提供的代码,这一段我实在是晕,不会。。。
import React,{useEffect,useState} from "react";
import { ethers } from "ethers";
import './App.css';
import abi from './utils/WavePortal.json';
export default function App() {
const [currentAccount,setCurrentAccount]=useState("");
const [totalWaves,setTotalWaves]=useState();
const [allwaves,setAllwaves]=useState([]);
const [inputValue,setInputValue]= useState('');
const contractAddress = "0xD014C8260bbF382D2ecCf0eb832c20A38c070F3b";
const contractABI = abi.abi;
const chainId = `0x5`;
const rpcURL = 'https://rpc.ankr.com/eth_goerli';
const networkName = 'Goerli Network';
const currencyName = 'ETH';
const currencySymbol = 'ETH';
const explorerURL = 'https://goerli.etherscan.io/';
const addNetwork = async () => {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: chainId,
chainName: networkName,
rpcUrls: [rpcURL],
blockExplorerUrls: [explorerURL],
nativeCurrency: {
name: currencyName,
symbol: currencySymbol, // 2-6 characters long
decimals: 18,
},
},
],
});
// refresh
window.location.reload();
};
const checkIfWalletIsConnected = async ()=>{
try {
const {ethereum} = window;
if(!ethereum){
console.log("make sure you have metamask");
return;
}else{
console.log("we have the etherum object")
}
const accounts = await ethereum.request({method: "eth_accounts"})
if(accounts.length!==0){
const account = accounts[0];
await getAllWaves();
setCurrentAccount(account);
}else{
console.log("No authorized account found")
}
} catch (error) {
console.log(error);
}
}
const wave = async () => {
try {
const {ethereum} = window;
if(ethereum){
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);
let count = await wavePortalContract.getTotalWaves();
console.log("Retrieved total wave count...", count.toNumber());
const waveTxn = await wavePortalContract.wave(inputValue,{gasLimit: 300000});
console.log("Mining...", waveTxn.hash);
await waveTxn.wait();
console.log("Mined -- ", waveTxn.hash);
count = await wavePortalContract.getTotalWaves();
// await getAllWaves();
setTotalWaves(count.toNumber());
console.log("Retrieved total wave count...", count.toNumber());
}else{
console.log("ethereum object doesn't exist")
}
} catch (error) {
console.log(error);
}
}
const connectWallet = async () => {
try {
const { ethereum } = window;
if (!ethereum) {
alert("Get MetaMask!");
return;
}
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
console.log("Connected", accounts[0]);
setCurrentAccount(accounts[0]);
} catch (error) {
console.log(error)
}
}
useEffect(()=>{
checkIfWalletIsConnected();
},[currentAccount])
const fetchTotal = async ()=>{
const {ethereum} = window;
if(ethereum){
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);
let count = await wavePortalContract.getTotalWaves();
setTotalWaves(count.toNumber());
console.log("Retrieved total wave count...", count.toNumber());
}else{
console.log("ethereum object doesn't exist")
}
}
useEffect(async ()=>{
await fetchTotal();
},[totalWaves])
const getAllWaves=async ()=>{
try {
if(ethereum){
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);
const waves = await wavePortalContract.getAllWaves();
console.log(waves);
let wavesArr = [];
waves.forEach(wave => {
wavesArr.push({
address: wave.waver,
timestamp: new Date(wave.timestamp * 1000),
message: wave.message
});
});
setAllwaves(wavesArr);
}
} catch (error) {
console.log(error);
}
}
useEffect(()=>{
let wavePortalContract;
const onNewWave = (from, timestamp, message) => {
console.log('NewWave', from, timestamp, message);
setAllwaves(prevState => [
...prevState,
{
address: from,
timestamp: new Date(timestamp * 1000),
message: message,
},
]);
};
if (window.ethereum) {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);
wavePortalContract.on('NewWave', onNewWave);
}
return () => {
if (wavePortalContract) {
wavePortalContract.off('NewWave', onNewWave);
}
};
},[])
return (
<div className="mainContainer">
<div className="dataContainer">
<div className="header">
<div>👋 Hey there!</div>
<div className="add" onClick={addNetwork}>Add Goerli to Wallet</div>
</div>
<div className="bio">
I am farza and I worked on self-driving cars so that's pretty cool right? Connect your Ethereum wallet and wave at me!
</div>
<div className="inputWrapper">
<input className="messageBox" placeholder="Please input wave message" onChange={(e)=>{
setInputValue(e.target.value);
}}></input>
<button className="waveButton" onClick={wave}>
Wave at Me
</button>
</div>
{!currentAccount&&(
<button className="connectButton" onClick={connectWallet}>Connect Wallet</button>
)}
<div>
TotalWaves: {totalWaves}
</div>
{allwaves.map((wave,index)=>{
return (
<div key={index} className="messageCard">
<div>Address: {wave.address}</div>
<div>Timestamp: {wave.timestamp.toString()}</div>
<div>Message: {wave.message}</div>
</div>
)
})}
</div>
</div>
);
}
8.再到这个界面点击测试一下,能跳出小狐狸,能生成用户信息就可以了,丑就对了,我又不懂这个哈哈,官方看了不是他们自己的截图就行。
9.直接去提交截图就可以了。
1.将APP.js换成这段代码。
import React, { useEffect, useState } from "react";
import { ethers } from "ethers";
import './App.css';
import wavePortal from './utils/WavePortal.json';
const App = () => {
const [currentAccount, setCurrentAccount] = useState("");
const [allWaves, setAllWaves] = useState([]);
const contractAddress = "0xd5f08a0ae197482FA808cE84E00E97d940dBD26E";
const getAllWaves = async () => {
try {
if (window.ethereum) {
const provider = new ethers.providers.Web3Provider
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(contractAddress, wavePortal.abi, signer);
const waves = await wavePortalContract.getAllWaves();
let wavesCleaned = [];
waves.forEach(wave => {
wavesCleaned.push({
address: wave.waver,
timestamp: new Date(wave.timestamp * 1000),
message: wave.message
});
});
setAllWaves(wavesCleaned);
} else {
console.log("Ethereum object doesn't exist!")
}
} catch (error) {
console.log(error);
}
}
const checkIfWalletIsConnected = async () => {
try {
const { ethereum } = window;
if (!ethereum) {
console.log("Make sure you have metamask!");
return;
} else {
console.log("We have the ethereum object", ethereum);
}
const accounts = await ethereum.request({ method: 'eth_accounts' });
if (accounts.length !== 0) {
const account = accounts[0];
console.log("Found an authorized account:", account);
setCurrentAccount(account)
} else {
console.log("No authorized account found")
}
} catch (error) {
console.log(error);
}
}
const connectWallet = async () => {
try {
const { ethereum } = window;
if (!ethereum) {
alert("Get MetaMask!");
return;
}
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
console.log("Connected", accounts[0]);
setCurrentAccount(accounts[0]);
} catch (error) {
console.log(error)
}
}
const wave = async () => {
try {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(contractAddress, wavePortal.abi, signer);
let count = await wavePortalContract.getTotalWaves();
console.log("Retrieved total wave count...", count.toNumber());
const waveTxn = await wavePortalContract.wave();
console.log("Mining...", waveTxn.hash);
await waveTxn.wait();
console.log("Mined -- ", waveTxn.hash);
count = await wavePortalContract.getTotalWaves();
console.log("Retrieved total wave count...", count.toNumber());
} else {
console.log("Ethereum object doesn't exist!");
}
} catch (error) {
console.log(error)
}
}
useEffect(() => {
checkIfWalletIsConnected();
}, [])
return (
<div className="mainContainer">
<div className="dataContainer">
<div className="header">
👋 Hey there!
</div>
<div className="bio">
I am farza and I worked on self-driving cars so that's pretty cool right? Connect your Ethereum wallet and wave at me!
</div>
<button className="waveButton" onClick={wave}>
Wave at Me
</button>
{!currentAccount && (
<button className="waveButton" onClick={connectWallet}>
Connect Wallet
</button>
)}
{allWaves.map((wave, index) => {
return (
<div style={{ backgroundColor: "OldLace", marginTop: "16px", padding: "8px" }}>
<div>Address: {wave.address}</div>
<div>Time: {wave.timestamp.toString()}</div>
<div>Message: {wave.message}</div>
</div>)
})}
</div>
</div>
);
}
export default App
2.将deploy.js换成这段代码。
const main = async () => {
const waveContractFactory = await hre.ethers.getContractFactory('WavePortal');
const waveContract = await waveContractFactory.deploy({
value: hre.ethers.utils.parseEther('0.001'),
});
await waveContract.deployed();
console.log('WavePortal address: ', waveContract.address);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
};
runMain();
3.将run.js换成这段代码。
const main = async () => {
const waveContractFactory = await hre.ethers.getContractFactory('WavePortal');
const waveContract = await waveContractFactory.deploy({
value: hre.ethers.utils.parseEther('0.01'),
});
await waveContract.deployed();
console.log('Contract addy:', waveContract.address);
let contractBalance = await hre.ethers.provider.getBalance(
waveContract.address
);
console.log(
'Contract balance:',
hre.ethers.utils.formatEther(contractBalance)
);
let waveTxn = await waveContract.wave('A message!');
await waveTxn.wait();
contractBalance = await hre.ethers.provider.getBalance(waveContract.addresss);
console.log(
'Contract balance:',
hre.ethers.utils.formatEther(contractBalance)
);
let allWaves = await waveContract.getAllWaves();
console.log(allWaves);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
4.将WavePortal.sol换成这段代码。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract WavePortal {
uint256 totalWaves;
event NewWave(address indexed from, uint256 timestamp, string message);
struct Wave {
address waver;
string message;
uint256 timestamp;
}
Wave[] waves;
constructor() payable {
console.log("We have been constructed!");
}
function wave(string memory _message) public {
totalWaves += 1;
console.log("%s has waved!", msg.sender);
waves.push(Wave(msg.sender, _message, block.timestamp));
emit NewWave(msg.sender, block.timestamp, _message);
uint256 prizeAmount = 0.0001 ether;
require(
prizeAmount <= address(this).balance,
"Trying to withdraw more money than they contract has."
);
(bool success, ) = (msg.sender).call{value: prizeAmount}("");
require(success, "Failed to withdraw money from contract.");
}
function getAllWaves() public view returns (Wave[] memory) {
return waves;
}
function getTotalWaves() public view returns (uint256) {
return totalWaves;
}
}
5.在控制台输入npx hardhat run scripts/run.js ,如图所示。
6.在控制台输入npx hardhat run scripts/deploy.js --network goerli 在生成一次地址。现在,当您转到 Etherscan 并粘贴您的合约地址时,您会看到您的合约现在有 0.001 ETH 的价值! 成功!
7.将这个地址提交即可。
1.将Waveportal的代码换成下面地代码。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract WavePortal {
uint256 totalWaves;
/*
* We will be using this below to help generate a random number
*/
uint256 private seed;
event NewWave(address indexed from, uint256 timestamp, string message);
struct Wave {
address waver;
string message;
uint256 timestamp;
}
Wave[] waves;
constructor() payable {
console.log("We have been constructed!");
/*
* Set the initial seed
*/
seed = (block.timestamp + block.difficulty) % 100;
}
function wave(string memory _message) public {
totalWaves += 1;
console.log("%s has waved!", msg.sender);
waves.push(Wave(msg.sender, _message, block.timestamp));
/*
* Generate a new seed for the next user that sends a wave
*/
seed = (block.difficulty + block.timestamp + seed) % 100;
console.log("Random # generated: %d", seed);
/*
* Give a 50% chance that the user wins the prize.
*/
if (seed < 50) {
console.log("%s won!", msg.sender);
/*
* The same code we had before to send the prize.
*/
uint256 prizeAmount = 0.0001 ether;
require(
prizeAmount <= address(this).balance,
"Trying to withdraw more money than the contract has."
);
(bool success, ) = (msg.sender).call{value: prizeAmount}("");
require(success, "Failed to withdraw money from contract.");
}
emit NewWave(msg.sender, block.timestamp, _message);
}
function getAllWaves() public view returns (Wave[] memory) {
return waves;
}
function getTotalWaves() public view returns (uint256) {
return totalWaves;
}
}
2.将run.js的代码换成下面的。
const main = async () => {
const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
const waveContract = await waveContractFactory.deploy({
value: hre.ethers.utils.parseEther("0.1"),
});
await waveContract.deployed();
console.log("Contract addy:", waveContract.address);
let contractBalance = await hre.ethers.provider.getBalance(
waveContract.address
);
console.log(
"Contract balance:",
hre.ethers.utils.formatEther(contractBalance)
);
/*
* Let's try two waves now
*/
const waveTxn = await waveContract.wave("This is wave #1");
await waveTxn.wait();
const waveTxn2 = await waveContract.wave("This is wave #2");
await waveTxn2.wait();
contractBalance = await hre.ethers.provider.getBalance(waveContract.address);
console.log(
"Contract balance:",
hre.ethers.utils.formatEther(contractBalance)
);
let allWaves = await waveContract.getAllWaves();
console.log(allWaves);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
3.再回到控制台,运行npx hardhat run scripts/run.js ,可以得到如下结果。数字是随机生成的,不需要一样啊。
4.再将Waveportal.sol的代码全部换成下面的。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
import "hardhat/console.sol";
contract WavePortal {
uint256 totalWaves;
uint256 private seed;
event NewWave(address indexed from, uint256 timestamp, string message);
struct Wave {
address waver;
string message;
uint256 timestamp;
}
Wave[] waves;
/*
* This is an address => uint mapping, meaning I can associate an address with a number!
* In this case, I'll be storing the address with the last time the user waved at us.
*/
mapping(address => uint256) public lastWavedAt;
constructor() payable {
console.log("We have been constructed!");
/*
* Set the initial seed
*/
seed = (block.timestamp + block.difficulty) % 100;
}
function wave(string memory _message) public {
/*
* We need to make sure the current timestamp is at least 15-minutes bigger than the last timestamp we stored
*/
require(
lastWavedAt[msg.sender] + 15 minutes < block.timestamp,
"Wait 15m"
);
/*
* Update the current timestamp we have for the user
*/
lastWavedAt[msg.sender] = block.timestamp;
totalWaves += 1;
console.log("%s has waved!", msg.sender);
waves.push(Wave(msg.sender, _message, block.timestamp));
/*
* Generate a new seed for the next user that sends a wave
*/
seed = (block.difficulty + block.timestamp + seed) % 100;
if (seed <= 50) {
console.log("%s won!", msg.sender);
uint256 prizeAmount = 0.0001 ether;
require(
prizeAmount <= address(this).balance,
"Trying to withdraw more money than they contract has."
);
(bool success, ) = (msg.sender).call{value: prizeAmount}("");
require(success, "Failed to withdraw money from contract.");
}
emit NewWave(msg.sender, block.timestamp, _message);
}
function getAllWaves() public view returns (Wave[] memory) {
return waves;
}
function getTotalWaves() public view returns (uint256) {
return totalWaves;
}
}
5.回到控制台运行npx hardhat run scripts/run.js 。如果跟刚才运行时间不超过15分钟,就会报错,这就对了。
6.然后提交即可。这里还是提交部署合约的以太坊链接。
1.再replit把网站的界面改一下,截图提交即可。
2.推特分享一下即可。
3.点击右下角get nft。
4.点击链接钱包,进去就可以得到了,等邮件通知吧。
终于做完了,累死我了从中午做到现在。。。总算拿到NFT,可以安心睡觉了,哈哈哈。