推特: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-portalcd my-wave-portalnpm init -ynpm 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 compilenpx 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.领水,以下三个链接都可以用,自取。
https://faucets.chain.link/goerli
https://app.mycrypto.com/faucet
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,可以安心睡觉了,哈哈哈。
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。