Cover photo

Buildspace Testnet Guide

Note :

This tutorial using Bahasa Indonesia, if you still wanna know, just translate it and good luck!

Bahan-bahan :

Aplikasi :

Tutorial (Step 1) :

  • Buka website ini kemudian klik β€œStart Build” dan login pakai akun Google-mu.

    https://buildspace.so/builds/solidity

  • Klik β€œAdd to Calendar” dan hubungkan akun Discord-mu, lalu klik β€œLets go!”

  • Pada fase β€œWelcome, let's get you the details!”, isi alasan atau motivasi kamu dalam mengikuti event ini. Gunakan bahasa inggris ya.

  • Siapkan command prompt (CMD) kamu, lalu lanjut ke fase β€œGet your local Ethereum networking running - 1” dan pastikan sudah menginstall NodeJS versi 16.

  • Ketik : node -v di CMD, apabila sudah versi 16 maka lanjut ke step berikutnya.

  • ketik ini :

mkdir my-wave-portal
cd my-wave-portal
npm init -y
npm install --save-dev hardhat@latest

Kalo hasilnya seperti berikut, maka lanjut ke step berikutnya ya.

post image
post image
  • Ketik npx hardhat lalu pilih β€œCreate javascript”, untuk folder biarkan default saja, kemudian tekan y dan enter.

  • setelah selesai, jalankan code berikut :

npm install --save-dev "hardhat@^2.12.2" "@nomicfoundation/hardhat-toolbox@^2.0.0"
  • maka hasilnya akan kurang lebih seperti ini :

post image
  • Buka aplikasi VS Code > File > Open Folder > Pilih folder β€œmy-wave-portal” yang udah kamu install sebelumnya.

  • Buat/edit file hardhat.config.js dan isi code berikut :

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",
};
  • Save, lalu run npx hardhat node di CMD kamu. Nanti disana akan muncul 15 address, TOLONG JANGAN DI CLOSE.

  • Buka CMD baru, lalu ketik code berikut :

cd my-wave-portal
npx hardhat compile
npx hardhat test
  • hasilnya akan seperti ini, kalo iya silakan lanjut.

post image
  • Kemudian kembali ke aplikasi VS Code dan silakan hapus file lock.js di folder test, deploy.js di folder scripts, dan lock.sol di folder contracts.

  • Setelah itu, submit screenshot dari CMD kamu yang berisi 15 alamat di step sebelumnya ke Discord official Buildspace di channel #progress (link Discord ada diatas ya)

  • Submit juga screenshot tersebut di website Buildspace dengan cara klik β€œSubmit Requirement”.

post image

Tutorial (Step 2) :

  • Buka aplikasi VS Code, buat file WavePortal.sol di dalam folder contracts isi dengan code berikut :

// 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");
    }
}

Tutorial (Step 3) :

  • Kembali ke aplikasi VS Code, buat file dengan nama run.js dan taruh didalam folder scripts kemudian isi dengan code berikut :

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();
  • Buka CMD kamu yang ke-2 (yang sedang nganggur), kemudian tulis command ini npx hardhat run scripts/run.js dan screenshot + submit hasil dari command tersebut di Discord + website Buildspace.

post image

Tutorial (Step 4) :

  • Edit file WavePortal.sol yang ada didalam folder contracts dan isi dengan code berikut :

// 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;
    }
}
  • Edit file run.js dan isi dengan code berikut :

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(); 
  • Kemudian buka CMD kamu lagi, dan run command ini npx hardhat run scripts/run.js dan hasilnya sebagai berikut :

post image
  • Setelah selesai, buka website Buildspace dan submit kembali. Untuk kata-katanya, isi dengan Create my first eth contract.

Tutorial (Step 5) :

  • Kembali ke aplikasi VS Code kemudian tambahkan/edit file deploy.js didalam folder scripts dengan code berikut :

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();
  • kemudian run command berikut (dengan catatan, posisi folder di CMD kamu sekarang berada didalam folder my-wave-portal) :

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

  • Copy WavePortal address, dan simpan di tempat terbaik.

  • Screenshot hasilnya, dan submit di website Buildspace.

post image

Tutorial (Step 6) :

post image
  • Setelah berhasil, silakan tekan RUN.

post image
  • Kemudian copy link seperti yang saya tunjuk, lalu submit di website Buildspace.

post image

Tutorial (Step 7) :

post image
  • Step 1 > Pilih Ethereum

  • Step 2 > Pilih Goerli

  • Step 3 > Tidak usah di edit, langsung tekan β€œContinue” saja.

  • Pada bagian β€œPlan”, pilih yang β€œDiscover”

  • Create!

post image
  • Setelah jadi, back-up link kamu di bagian β€œHTTP Provider”

post image
  • Kembali ke CMD kamu yang nganggur, ketik npm install --save dotenv

  • Kembali ke aplikasi VS Code, edit file hardhat.config.js dan isi dengan code berikut :

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],
    },
  },
};
  • Setelah itu, kembali ke CMD dan ketik echo test>.env, hasilnya seperti berikut :

post image
  • Kemudian cek di aplikasi VS Code, nanti akan ada muncul file baru dengan nama .env, edit file tersebut dan isi ini :

STAGING_QUICKNODE_KEY=isi_dengan_https_dari_QuickNode_tadi
PRIVATE_KEY=isi_dengan_private_key_metamask_kamu
  • Save, kemudian run command npx hardhat run scripts/deploy.js --network goerli di CMD dan hasilnya seperti :

post image
  • Silakan buka website berikut dan ambil LINK TRANSAKSI HASH kamu disana :

    https://goerli.etherscan.io/

  • Submit link tersebut di website Buildspace

Tutorial (Step 8) :

  • Kembali buka akun Replit kamu, buka folder src dan edit file App.jsx dan replace code tersebut dengan ini ya ges ya :

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;
  • Tekan β€œRUN” kembali, seperti di step sebelumnya.

  • Nanti hasilnya seperti ini :

post image
  • Silakan koneksikan wallet yang private-key nya sudah kamu submit tadi di website tersebut.

  • Buka website Buildspace kembali, klik β€œSubmit Requirement” dan isi form tersebut dengan kalimat indahmu ;)

Tutorial (Step 9) :

  • Kembali ke Replit lagi, kita edit file App.jsx lagi dan isi dengan code berikut :

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
  • Pada bagian const contractAddress, silakan ganti 0xblablabla dengan address WavePortal yang kita dapat dari step tutorial 5.

  • Kembali ke Replit lagi, silakan buat folder dengan nama src didalam folder utils, dan buat file dengan nama `WavePortal.json`.

post image
  • Kembali ke VS Code lalu buka file ini artifacts/contracts/WavePortal.sol/WavePortal.json, kemudian ambil isinya dan paste ke file di Replit yang baru kita buat tadi.

  • Setelah itu, buka website yang kamu generate di Replit, klik β€œConnect”, dan klik β€œWave at Me”.

  • Terima approval yang muncul di Metamask jaringan GOERLI.

  • Screenshot website kamu, dan upload di website Buildspace <3

post image

Tutorial (Step 10) :

  • Kembali ke VS Code, dan edit file WavePortal.sol yang kita buat diawal tadi.

// 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;
    }
}
  • Edit file run.js dengan ini :

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();
  • Buka CMD kamu yang nganggur, dan ketik command ini npx hardhat run scripts/run.js dan liat hasilnya.

post image
  • Kita run ulang command berikut dan SAVE WavePortal address kamu : npx hardhat run scripts/deploy.js --network goerli

  • Buka Replit lagi, dan edit contractAddress di file App.js dan replace dengan WavePortal yang baru kamu generate.

  • (OPSIONAL) kamu bisa pakai code lain di App.js, semisal ingin membuat tampilannya lebih baik ~ Berikut codenya (INGAT, INI OPSIONAL) :

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>
  );
}
  • Buka website hasil generate di Replit, dan liat tampilannya sekarang.

  • Silakan screenshot dan submit di Discord dan website Buildspace.

post image

Tutorial (Step 11) :

  • Kembali ke Replit, edit file App.js dengan code berikut :

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
  • Buka aplikasi VS Code di komputermu, dan edit deploy.js jadi begini :

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();
  • Masih di VS Code, edit file run.js jadi begini :

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.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();
  • Masih di VS Code ya ges ya, edit file WavePortal.sol dengan code :

// 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;
    }
}
  • Run command berikut di CMD, untuk ngecek hasil dari code-code diatas :

    npx hardhat run scripts/run.js

  • Hasilnya seperti ini :

post image

Tutorial (Step 12) :

  • Kembali ke VSC, edit file WavePortal.sol dengan ini :

// 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;
    }
}
  • Edit run.js dengan ini :

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();
  • Kemudian run command berikut di CMD kamu yang nganggur : npx hardhat run scripts/run.js

  • Hasilnya :

post image
  • Kita edit kembali file WavePortal.sol dan isi dengan ini :

// 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;
    }
}
  • Setelah itu, buka CMD dan run ini : npx hardhat run scripts/run.js

  • Nanti disana akan ada error, silakan tunggu 15 menit kemudian RUN lagi. Contoh bisa diliat sebagai berikut :

post image
  • Setelah kamu run lagi dan hash-nya muncul, silakan ke explorer Goerli dan copy link-nya. Kemudian, submit di website Buildspace.

Tutorial (Step 13 - Terakhir) :

  • Buka website hasil generate di Replit, kemudian edit sesuka hati.

  • Screenshot, submit di website Buildspace.

post image
  • Done!

Catatan :

  1. Jadi kalian perlu ngebuka setidaknya 2-3 command prompt, 1 untuk yg idle atau standby, sisanya untuk ngegarap semua command yang ada.

  2. Pengiriman hadiah NFT dilakukan dalam 7 hari.

  3. Jika ada pertanyaan, silakan tanyakan di grup Telegram :

    https://t.me/HappyCuanAirdrop

Credits :

https://mirror.xyz/0xCD0e394639B2D0b159B41F9dBe0583C33d85e874/1EJbrgGMc44-Pwoimu2DrjTiDgN4uevTMPTF2upeyTQ