How to Build And Deploy a Modern Web3 Blockchain App – Web3 Tutorial


In this Web3 tutorial, we’ll be building a fully-functional blockchain, web3 App. We’ll be starting with the basics of building a blockchain app and at the end, we’ll get our web3 app live for everyone to use.

What Are We Going to Build?

We’ll be building a Decentralized App(dApp) called 🧀 Pick Up Lines. As the name suggests, our users will be able to send some good ol’ pickup lines and stand a chance to win Ethereum as a reward.

Prerequisites

  • Beginner to intermediate knowledge of React
  • Some familiarity with Solidity smart contracts
  • Basic understanding of blockchain programming

Work becomes play with the right tools, right? Fortunately, web3 has a plethora of tools at its disposal to achieve the infamous WAGMI 🧘

  1. Visual Studio Code or any text editor
  2. Hardhat for Ethereum development
  3. Metamask as a crypto wallet
  4. Vercel and Alchemy as hosting platforms

Let’s Begin!

Now, that we have some idea of the final app and the tools that we’re going to use, let’s start writing code!

First, we’ll write the smart contract of our blockchain app. Then, we’ll build our React app, and at the end, connect those two things to have a full-fledged web3 app.

what are we waiting for south park gif

Writing our Smart Contract in Solidity:

1. Get our Local Ethereum Network Running

We need to spin up a local Ethereum network. A local Ethereum network is a blockchain network that specifically runs on your local machine. We’ll use it for testing while we build our application, as it gives us all the blockchain features without using real cryptocurrency.

In this web3 tutorial, we’ll be using Hardhat. Since we need to test our blockchain app locally before launching, we’ll use fake ETH and fake test accounts to test our smart contract through Hardhat. Most importantly, it will facilitate compiling our smart contract on our local blockchain.

Now, head to the terminal and move to the directory you want. Once there, run these commands:

mkdir pickup-lines
cd pickup-lines
npm init -y
npm install --save-dev hardhat

Next, let’s get a sample project running:


npx hardhat

Run the project:


npx hardhat compile

Test the project:


npx hardhat test

The code above sets up a barebone Hardhat project. With no plugins, it allows you to create your own tasks, compile your Solidity code, and run your tests. Basically, you’re creating a blockchain network in a local environment

You’ll see something similar to this:
Output of the terminal after running tests on the project

2. Creating our Smart Contract

Now, let’s create a PickupLines.sol file under the Contracts directory.

We need to follow a strict folder structure. It’s super important because we’re building on top of Hardhat, and the default paths for our /contracts, /scripts and /test are pre-defined. Not following this structure will lead to our Hardhat tasks failing. Be careful!





pragma solidity ^0.8.0;


import "hardhat/console.sol";


contract PickupLines 
   
    constructor() 
        console.log("I am the Cheesy PickUp Lines' smart contract.");
    

3. Running Our Contract Locally

Now, let’s create a script to run the smart contract we just built. This will enable us to test it on our local blockchain.

Go into the scripts folder and create a file named run.js. In the run.js file, enter the following code:


const main = async () => 
  
  const contracts = await hre.ethers.getContractFactory("PickupLines"); 

  
  const contract = await contracts.deploy();
  await contract.deployed();

  
  console.log("Contract deployed to:", contract.address);
;


const runMain = async () => 
  try 
    await main();
    process.exit(0); 
   catch (error) 
    console.log(error);
    process.exit(1); 
  
;


runMain();

Let’s run the run.jsfile we just created from our terminal:


npx hardhat run scripts/run.js

Did that go well? You can see the console.log message we put in our constructor() method. There, you’ll also see the contract address too

Output of the terminal after the script run

4. Finishing the Smart Contract Logic

Now, let’s make our contract a bit fancier.

We want to be able to let someone send us a pickup line and then store that line in the blockchain. So, the first thing we need is a function, so anyone can send us a pickup line.

In the PickupLines.sol file under the Contracts folder, enter the following code:

contract PickUpLines 
    
    event NewPickUpLine(address indexed from, uint256 timestamp, string line);

    
    uint256 private seed; 
    uint256 totalLines; 
    mapping(address => bool) hasWrote; 

    
    struct PickUpLine 
        address writer;
        string line;
        uint256 timestamp;
    
    
    PickUpLine[] pickuplines;

    constructor() payable 
      console.log("I am the Cheesy PickUp Lines' smart contract!");
    

    
    function newLine(string memory _line) public 

        
        totalLines += 1;
        pickuplines.push(PickUpLine(msg.sender, _line, block.timestamp));
        hasWrote[msg.sender] = true;
        emit NewPickUpLine(msg.sender, block.timestamp, _line);
    

    
    function getTotalLines() public view returns (uint256) 
        console.log("We have %s total PickUpLines.", totalLines);
        return totalLines;
    

Boom! So, that’s how a function is written in Solidity.

We also added a totalLines variable that is automatically initialized to 0. This variable is special because it’s called a state variable, and it’s a special one because it’s stored permanently in our contract storage.

5. Deploying the Smart Contract

Now, we’ll upgrade from our local blockchain to a globally-accessible blockchain.

Follow the 4 steps below:

1. Let’s create a file called deploy.js inside the scripts folder. Enter the code given below in the deploy.js file we just created.

   
   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 contracts = await hre.ethers.getContractFactory("PickupLines");
  const contract = await contracts.deploy();
  await contract.deployed();

  
  console.log("PickupLines address: ", contract.address);
;


const runMain = async () => 
  try 
    await main();
    process.exit(0);
   catch (error) 
    console.log(error);
    process.exit(1);
  
;


runMain();

2. We’ll be using a platform called Alchemy. Sign up [here]. (alchemy.com).

We’ll be using Alchemy to deploy our contract on the testnet. This is because if we use the Ethereum Mainnet, then every action/transaction on our app will have a real monetary value. We don’t want to do that until our app is fully developed for public usage. For now, we’re just testing our app.

With a testnet, we’ll be able to enjoy all the functions of a blockchain, albeit with fake cryptocurrency. You can get some fake ETH here.

Learn more about Alchemy right here. 👇

%[youtu.be/e-I1PPIwyqc]

3. This is the final part of the deployment. Make changes to your hardhat.config.js file by entering the code below:

 
require("@nomiclabs/hardhat-waffle");

module.exports = 
  solidity: "0.8.0",
  networks: 
    rinkeby: 
      url: "YOUR_ALCHEMY_API_URL",
      accounts: ["YOUR_WALLET_ACCOUNT_KEY"]
    ,
  ,
;

Note: Accessing your private key can be done by opening MetaMask, changing the network to “Rinkeby Test Network”, and then clicking the three dots and selecting “Account Details” > “Export Private Key”.

4. Now, we’re going to deploy the contract. We can do that by moving to the terminal and running the following command:

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

Did it work? EPIC.

Terminal output on the deployment of the smart contract on rinkeby testnet

We deployed the contract, and we also have its address on the blockchain. Note the address somewhere, as our website is going to need this so that it knows where to look on the blockchain for your contract.

Building the Web App in React

1. Setup a Basic React App with Metamask

It’s time to start working on our web app. Our contract was pretty simple. Now, let’s figure out how the front-end app can interact with our smart contract.

Note: We’ve built a starter kit for you! Here’s the link to the repository. You can clone the repository and start working.

The purpose of this blog post is to get you accustomed to blockchain development. We won’t be going too deep into the front-end development in this section.

2. Connect the Wallet to our App

Next, we need an Ethereum wallet. There’s many available, but for this project, we’re going to use Metamask. Download its browser extension and set up your wallet here.

In the App.tsx file inside the src folder, enter the following code:

import React,  useEffect, useState   from "react";
import  ethers  from "ethers";
import './App.css';


const contractAddress = '0x52BB......';

import abi from "./utils/PickupLines.json";

export default function App() {
  
  const [currentAccount, setCurrentAccount] = useState("");

  
  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)
    
  

 
  useEffect(() => 
    checkIfWalletIsConnected();
  , []);

  return (
    <div className="mainContainer">
      <div className="dataContainer">
        <div className="header">
        🧀 Hey there!
        </div>
        <div className="bio">
        <span>Welcome to Pick-Up Lines!</span>
        <button className="button" onClick=null>
          Shoot Line
        </button>

        
        !currentAccount && (
          <button className="button" onClick=connectWallet>
            Connect Wallet
          </button>
        )
      </div>
    </div>
    </div>
  );

3. Call the Smart Contract From our App

We now have a front-end app. We’ve deployed our contract. We’ve connected our wallets. Now let’s call our contract from the front-end side using the credentials we have access to from Metamask.

Add the following pickup function in our App component inside the App.tsx file:

const pickup = async () => 
    try 
      const  ethereum  = window;

      if (ethereum) 
        const provider = new ethers.providers.Web3Provider(ethereum);
        const signer = provider.getSigner();
        const contract = new ethers.Contract(contractAddress, contractABI, signer);

        
        let count = await contract.getTotalLines();
        console.log("Retrieved total lines...", count.toNumber());

        
        const contractTxn = await contract.wave();
        console.log("Mining...", contractTxn.hash);

        await contractTxn.wait();
        console.log("Mined -- ", contractTxn.hash);

        
        count = await contract.getTotalLines();
        console.log("Retrieved total lines count...", count.toNumber());
       else 
        console.log("Ethereum object doesn't exist!");
      
     catch (error) 
      console.log(error);
    

Now to call the function, let’s create a button in the App.tsx file. Add the following code:

<button className="button" onClick=pickup>
    Send a line
</button>

When you run this, you’ll see that the total line count is increased by 1. You’ll also see that Metamask pops us and asks us to pay “gas” which we pay by using our fake crypto.

Send lucky users some Ethereum

1. Set a Prize and Select Users to Send Them Ethereum

So right now, our code is set to store random pick up lines every single time. Let’s make it more interesting by adding a reward algorithm inside our newLine function. Modify the newLine function in the PickupLines.sol file:

function newLine(string memory _line) public 
  if (hasWrote[msg.sender]) 
    revert(
      "It seems you've posted a line already. We don't do repeats when it comes to picking up lines!"
     );
   
  
  totalLines += 1;
  pickuplines.push(PickUpLine(msg.sender, _line, block.timestamp));
  hasWrote[msg.sender] = true;
  emit NewPickUpLine(msg.sender, block.timestamp, _line);

 
 seed = (block.difficulty + block.timestamp + seed) % 100;
 if (seed <= 10) 
  uint256 prizeAmount = 0.0001 ether;
  require(
    prizeAmount <= address(this).balance,
    "The contract has insufficient ETH balance."
  );
  (bool success, ) = (msg.sender).callvalue: prizeAmount("");
  require(success, "Failed to withdraw ETH from the contract");
  

Here, the algorithm needs a random number. We take two numbers given to us by Solidity, block.difficulty and block.timestamp, and combine them to create a random number.

To make this random number even more random, we’ll create a variable seed that will essentially change every time a user sends a new line. We combined all three of these variables to generate a new random seed, %100.

Now, we need the value to lie in the range of 0-99 and in order to achieve that, we’ll use the Modulo operator(%) by applying seed % 100.

Final Steps

1. Preventing spammers

Now, you have a way to randomly select people to reward. It’s useful to add a condition to your site so that people can’t just spam pickup lines at you.

Why? Well, maybe you just don’t want them to keep on trying to win the reward over and over by sending multiple lines at you. Or, maybe you don’t want just their messages filling up your wall of messages. Modify the newLine function inside the PickupLines.sol file:

contract PickUpLines 
    mapping(address => bool) hasWrote;

    struct PickUpLine 
        address writer;
        string line;
        uint256 timestamp;
    
    PickUpLine[] pickuplines;

   
    function newLine(string memory _line) public 

       
        if (hasWrote[msg.sender]) 
            revert(
                "It seems you've posted a line already. We don't do repeats when it comes to picking up lines!"
            );
        

        hasWrote[msg.sender] = true;
    

2. Finalize

Congratulations! You’ve got all the core functionality down.

Now, it’s time for you to make this your own. Change up the CSS, the text, add some media embeds, add some more functionality, whatever. Make stuff look cool :).

✍🏻 Conclusion.

Build more web3 projects after this web3 tutorial.

There’s always multiple improvements/features one can think of with this blockchain app. Feel free to experiment with the code to improve your skills.

This blog is a part of the Hashnode Web3 blog, where a team of curated writers are bringing out new resources to help you discover the universe of web3. Check us out for more on NFTs, blockchains, and the decentralized future.



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *