Build a simple whitelist dApp using Solidity, Next.js, ethers.js on Ethereum

You might be launching your NFT assortment named Crypto Devs. You need to give your early supporters entry to a whitelist in your assortment, so right here you’re making a whitelist dapp for Crypto Devs



Necessities

  • Whitelist entry must be given to the primary 10 customers free of charge who need to get in.
  • There must be an internet site the place folks can go and enter into the whitelist.

Lets begin constructing πŸš€




Conditions

  • You’ll be able to write code in JavaScript (Newbie Monitor – Level-0)
  • Have arrange a Metamask Pockets (Newbie Monitor – Level-4)
  • Your pc has Node.js put in. If not obtain from here



Desire a Video?

When you would fairly be taught from a video, now we have a recording accessible of this tutorial on our YouTube. Watch the video by clicking on the screenshot beneath, or go forward and skim the tutorial!
Whitelist dApp Part-1
Whitelist dApp Part-2



Construct



Sensible Contract

To construct the sensible contract we might be utilizing Hardhat.
Hardhat is an Ethereum growth setting and framework designed for full stack growth in Solidity. In easy phrases you may write your sensible contract, deploy them, run checks, and debug your code.

  • First, it is advisable create a Whitelist-Daap folder the place the Hardhat venture and your Subsequent.js app will later go
  • Open up a terminal and execute these instructions
  mkdir Whitelist-Dapp
  cd Whitelist-Dapp
Enter fullscreen mode

Exit fullscreen mode

  • Then, in Whitelist-Daap folder, you’ll arrange Hardhat venture
  mkdir hardhat-tutorial
  cd hardhat-tutorial
  npm init --yes
  npm set up --save-dev hardhat
Enter fullscreen mode

Exit fullscreen mode

  • In the identical listing the place you put in Hardhat run:
  npx hardhat
Enter fullscreen mode

Exit fullscreen mode

  • Choose Create a fundamental pattern venture
  • Press enter for the already specified Hardhat Mission root
  • Press enter for the query on if you wish to add a .gitignore
  • Press enter for Do you need to set up this pattern venture's dependencies with npm (@nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers)?

Now you’ve gotten a hardhat venture able to go!

If you’re not on mac, please do that further step and set up these libraries as nicely πŸ™‚

npm set up --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
Enter fullscreen mode

Exit fullscreen mode

  • Begin by creating a brand new file contained in the contracts listing referred to as Whitelist.sol.
  //SPDX-License-Identifier: Unlicense
  pragma solidity ^0.8.0;


  contract Whitelist {

      // Max variety of whitelisted addresses allowed
      uint8 public maxWhitelistedAddresses;

      // Create a mapping of whitelistedAddresses
      // if an deal with is whitelisted, we'd set it to true, it's false by default for all different addresses.
      mapping(deal with => bool) public whitelistedAddresses;

      // numAddressesWhitelisted can be used to maintain monitor of what number of addresses have been whitelisted
      // NOTE: Do not change this variable identify, as it will likely be a part of verification
      uint8 public numAddressesWhitelisted;

      // Setting the Max variety of whitelisted addresses
      // Consumer will put the worth on the time of deployment
      constructor(uint8 _maxWhitelistedAddresses) {
          maxWhitelistedAddresses =  _maxWhitelistedAddresses;
      }

      /**
          addAddressToWhitelist - This operate provides the deal with of the sender to the
          whitelist
       */
      operate addAddressToWhitelist() public {
          // verify if the consumer has already been whitelisted
          require(!whitelistedAddresses[msg.sender], "Sender has already been whitelisted");
          // verify if the numAddressesWhitelisted < maxWhitelistedAddresses, if not then throw an error.
          require(numAddressesWhitelisted < maxWhitelistedAddresses, "Extra addresses cant be added, restrict reached");
          // Add the deal with which referred to as the operate to the whitelistedAddress array
          whitelistedAddresses[msg.sender] = true;
          // Improve the variety of whitelisted addresses
          numAddressesWhitelisted += 1;
      }

  }
Enter fullscreen mode

Exit fullscreen mode

  • Lets deploy the contract to rinkeby community.Create a brand new file named deploy.js underneath the scripts folder

  • Now we are going to write some code to deploy the contract in deploy.js file.

  const { ethers } = require("hardhat");

  async operate essential() {
    /*
    A ContractFactory in ethers.js is an abstraction used to deploy new sensible contracts,
    so whitelistContract here's a manufacturing facility for cases of our Whitelist contract.
    */
    const whitelistContract = await ethers.getContractFactory("Whitelist");

    // right here we deploy the contract
    const deployedWhitelistContract = await whitelistContract.deploy(10);
    // 10 is the Most variety of whitelisted addresses allowed

    // Anticipate it to complete deploying
    await deployedWhitelistContract.deployed();

    // print the deal with of the deployed contract
    console.log(
      "Whitelist Contract Tackle:",
      deployedWhitelistContract.deal with
    );
  }

  // Name the principle operate and catch if there's any error
  essential()
    .then(() => course of.exit(0))
    .catch((error) => {
      console.error(error);
      course of.exit(1);
    });
Enter fullscreen mode

Exit fullscreen mode

  • Now create a .env file within the hardhat-tutorial folder and add the next strains, use the directions within the feedback to get your Alchemy API Key URL and RINKEBY Non-public Key. Be sure that the account from which you get your rinkeby personal key’s funded with Rinkeby Ether.

  // Go to https://www.alchemyapi.io, join, create
  // a brand new App in its dashboard and choose the community as Rinkeby, and exchange "add-the-alchemy-key-url-here" with its key url
  ALCHEMY_API_KEY_URL="add-the-alchemy-key-url-here"

  // Change this personal key along with your RINKEBY account personal key
  // To export your personal key from Metamask, open Metamask and
  // go to Account Particulars > Export Non-public Key
  // Concentrate on NEVER placing actual Ether into testing accounts
  RINKEBY_PRIVATE_KEY="add-the-rinkeby-private-key-here"

Enter fullscreen mode

Exit fullscreen mode

  • Now we are going to set up dotenv package deal to have the ability to import the env file and use it in our config. Open up a terminal pointing athardhat-tutorial listing and execute this command
  npm set up dotenv
Enter fullscreen mode

Exit fullscreen mode

  • Now open the hardhat.config.js file, we’d add the rinkeby community right here in order that we will deploy our contract to rinkeby. Change all of the strains within the hardhar.config.js file with the given beneath strains
  require("@nomiclabs/hardhat-waffle");
  require("dotenv").config({ path: ".env" });

  const ALCHEMY_API_KEY_URL = course of.env.ALCHEMY_API_KEY_URL;

  const RINKEBY_PRIVATE_KEY = course of.env.RINKEBY_PRIVATE_KEY;

  module.exports = {
    solidity: "0.8.4",
    networks: {
      rinkeby: {
        url: ALCHEMY_API_KEY_URL,
        accounts: [RINKEBY_PRIVATE_KEY],
      },
    },
  };
Enter fullscreen mode

Exit fullscreen mode

  • Compile the contract, open up a terminal pointing athardhat-tutorial listing and execute this command
     npx hardhat compile
Enter fullscreen mode

Exit fullscreen mode

  • To deploy, open up a terminal pointing athardhat-tutorial listing and execute this command
  npx hardhat run scripts/deploy.js --network rinkeby
Enter fullscreen mode

Exit fullscreen mode

  • Save the Whitelist Contract Tackle that was printed in your terminal in your notepad, you would want it futher down within the tutorial.



Web site

  • To develop the web site we are going to use React and Next Js. React is a javascript framework used to make web sites and Subsequent.js is a React framework that additionally permits writing backend APIs code together with the frontend, so you do not want two separate frontend and backend companies.
  • First, You’ll need to create a brand new subsequent app. Your folder construction ought to look one thing like
  - Whitelist-Dapp
      - hardhat-tutorial
      - my-app
Enter fullscreen mode

Exit fullscreen mode

  • To create this next-app, within the terminal level to Whitelist-Dapp folder and kind
  npx create-next-app@newest
Enter fullscreen mode

Exit fullscreen mode

and press enter for all of the questions

  • Now to run the app, execute these instructions within the terminal
  cd my-app
  npm run dev
Enter fullscreen mode

Exit fullscreen mode

  • Now go to http://localhost:3000, your app must be working 🀘

  • Now lets set up Web3Modal library. Web3Modal is a simple to make use of library to assist builders simply enable their customers to connect with your dApps with all kinds of various wallets. By default Web3Modal Library helps injected suppliers like (Metamask, Dapper, Gnosis Protected, Body, Web3 Browsers, and so forth) and WalletConnect, You may as well simply configure the library to help Portis, Fortmatic, Squarelink, Torus, Authereum, D’CENT Pockets and Arkane.
    Open up a terminal pointing atmy-app listing and execute this command

  npm set up web3modal
Enter fullscreen mode

Exit fullscreen mode

  • In the identical terminal additionally set up ethers.js
  npm set up ethers
Enter fullscreen mode

Exit fullscreen mode

  • In your my-app/public folder, obtain this image and rename it to crypto-devs.svg
  • Now go to types folder and exchange all of the contents of Residence.modules.css file with the next code, this may add some styling to your dapp:
  .essential {
    min-height: 90vh;
    show: flex;
    flex-direction: row;
    justify-content: heart;
    align-items: heart;
    font-family: "Courier New", Courier, monospace;
  }

  .footer {
    show: flex;
    padding: 2rem 0;
    border-top: 1px stable #eaeaea;
    justify-content: heart;
    align-items: heart;
  }

  .picture {
    width: 70%;
    top: 50%;
    margin-left: 20%;
  }

  .title {
    font-size: 2rem;
    margin: 2rem 0;
  }

  .description {
    line-height: 1;
    margin: 2rem 0;
    font-size: 1.2rem;
  }

  .button {
    border-radius: 4px;
    background-color: blue;
    border: none;
    coloration: #ffffff;
    font-size: 15px;
    padding: 20px;
    width: 200px;
    cursor: pointer;
    margin-bottom: 2%;
  }
  @media (max-width: 1000px) {
    .essential {
      width: 100%;
      flex-direction: column;
      justify-content: heart;
      align-items: heart;
    }
  }
Enter fullscreen mode

Exit fullscreen mode

  • Open your index.js file underneath the pages folder and paste the next code, rationalization of the code will be discovered within the feedback. Be sure you examine React and React Hooks, React Hooks Tutorial in case you are not conversant in them.
  import Head from "subsequent/head";
  import types from "../types/Residence.module.css";
  import Web3Modal from "web3modal";
  import { suppliers, Contract } from "ethers";
  import { useEffect, useRef, useState } from "react";
  import { WHITELIST_CONTRACT_ADDRESS, abi } from "../constants";

  export default operate Residence() {
    // walletConnected preserve monitor of whether or not the consumer's pockets is related or not
    const [walletConnected, setWalletConnected] = useState(false);
    // joinedWhitelist retains monitor of whether or not the present metamask deal with has joined the Whitelist or not
    const [joinedWhitelist, setJoinedWhitelist] = useState(false);
    // loading is about to true after we are ready for a transaction to get mined
    const [loading, setLoading] = useState(false);
    // numberOfWhitelisted tracks the variety of addresses's whitelisted
    const [numberOfWhitelisted, setNumberOfWhitelisted] = useState(0);
    // Create a reference to the Web3 Modal (used for connecting to Metamask) which persists so long as the web page is open
    const web3ModalRef = useRef();

    /**
     * Returns a Supplier or Signer object representing the Ethereum RPC with or with out the
     * signing capabilities of metamask connected
     *
     * A `Supplier` is required to work together with the blockchain - studying transactions, studying balances, studying state, and so forth.
     *
     * A `Signer` is a particular sort of Supplier utilized in case a `write` transaction must be made to the blockchain, which entails the related account
     * needing to make a digital signature to authorize the transaction being despatched. Metamask exposes a Signer API to permit your web site to
     * request signatures from the consumer utilizing Signer capabilities.
     *
     * @param {*} needSigner - True in the event you want the signer, default false in any other case
     */
    const getProviderOrSigner = async (needSigner = false) => {
      // Connect with Metamask
      // Since we retailer `web3Modal` as a reference, we have to entry the `present` worth to get entry to the underlying object
      const supplier = await web3ModalRef.present.join();
      const web3Provider = new suppliers.Web3Provider(supplier);

      // If consumer shouldn't be related to the Rinkeby community, allow them to know and throw an error
      const { chainId } = await web3Provider.getNetwork();
      if (chainId !== 4) {
        window.alert("Change the community to Rinkeby");
        throw new Error("Change community to Rinkeby");
      }

      if (needSigner) {
        const signer = web3Provider.getSigner();
        return signer;
      }
      return web3Provider;
    };

    /**
     * addAddressToWhitelist: Provides the present related deal with to the whitelist
     */
    const addAddressToWhitelist = async () => {
      strive {
        // We'd like a Signer right here since it is a 'write' transaction.
        const signer = await getProviderOrSigner(true);
        // Create a brand new occasion of the Contract with a Signer, which permits
        // replace strategies
        const whitelistContract = new Contract(
          WHITELIST_CONTRACT_ADDRESS,
          abi,
          signer
        );
        // name the addAddressToWhitelist from the contract
        const tx = await whitelistContract.addAddressToWhitelist();
        setLoading(true);
        // look ahead to the transaction to get mined
        await tx.wait();
        setLoading(false);
        // get the up to date variety of addresses within the whitelist
        await getNumberOfWhitelisted();
        setJoinedWhitelist(true);
      } catch (err) {
        console.error(err);
      }
    };

    /**
     * getNumberOfWhitelisted:  will get the variety of whitelisted addresses
     */
    const getNumberOfWhitelisted = async () => {
      strive {
        // Get the supplier from web3Modal, which in our case is MetaMask
        // No want for the Signer right here, as we're solely studying state from the blockchain
        const supplier = await getProviderOrSigner();
        // We hook up with the Contract utilizing a Supplier, so we are going to solely
        // have read-only entry to the Contract
        const whitelistContract = new Contract(
          WHITELIST_CONTRACT_ADDRESS,
          abi,
          supplier
        );
        // name the numAddressesWhitelisted from the contract
        const _numberOfWhitelisted = await whitelistContract.numAddressesWhitelisted();
        setNumberOfWhitelisted(_numberOfWhitelisted);
      } catch (err) {
        console.error(err);
      }
    };

    /**
     * checkIfAddressInWhitelist: Checks if the deal with is in whitelist
     */
    const checkIfAddressInWhitelist = async () => {
      strive {
        // We are going to want the signer later to get the consumer's deal with
        // Despite the fact that it's a learn transaction, since Signers are simply particular sorts of Suppliers,
        // We are able to use it in it is place
        const signer = await getProviderOrSigner(true);
        const whitelistContract = new Contract(
          WHITELIST_CONTRACT_ADDRESS,
          abi,
          signer
        );
        // Get the deal with related to the signer which is related to  MetaMask
        const deal with = await signer.getAddress();
        // name the whitelistedAddresses from the contract
        const _joinedWhitelist = await whitelistContract.whitelistedAddresses(
          deal with
        );
        setJoinedWhitelist(_joinedWhitelist);
      } catch (err) {
        console.error(err);
      }
    };

    /*
      connectWallet: Connects the MetaMask pockets
    */
    const connectWallet = async () => {
      strive {
        // Get the supplier from web3Modal, which in our case is MetaMask
        // When used for the primary time, it prompts the consumer to attach their pockets
        await getProviderOrSigner();
        setWalletConnected(true);

        checkIfAddressInWhitelist();
        getNumberOfWhitelisted();
      } catch (err) {
        console.error(err);
      }
    };

    /*
      renderButton: Returns a button based mostly on the state of the dapp
    */
    const renderButton = () => {
      if (walletConnected) {
        if (joinedWhitelist) {
          return (
            <div className={types.description}>
              Thanks for becoming a member of the Whitelist!
            </div>
          );
        } else if (loading) {
          return <button className={types.button}>Loading...</button>;
        } else {
          return (
            <button onClick={addAddressToWhitelist} className={types.button}>
              Be a part of the Whitelist
            </button>
          );
        }
      } else {
        return (
          <button onClick={connectWallet} className={types.button}>
            Join your pockets
          </button>
        );
      }
    };

    // useEffects are used to react to modifications in state of the web site
    // The array on the finish of operate name represents what state modifications will set off this impact
    // On this case, each time the worth of `walletConnected` modifications - this impact might be referred to as
    useEffect(() => {
      // if pockets shouldn't be related, create a brand new occasion of Web3Modal and join the MetaMask pockets
      if (!walletConnected) {
        // Assign the Web3Modal class to the reference object by setting it is `present` worth
        // The `present` worth is continued all through so long as this web page is open
        web3ModalRef.present = new Web3Modal({
          community: "rinkeby",
          providerOptions: {},
          disableInjectedProvider: false,
        });
        connectWallet();
      }
    }, [walletConnected]);

    return (
      <div>
        <Head>
          <title>Whitelist Dapp</title>
          <meta identify="description" content material="Whitelist-Dapp" />
          <hyperlink rel="icon" href="/favicon.ico" />
        </Head>
        <div className={types.essential}>
          <div>
            <h1 className={types.title}>Welcome to Crypto Devs!</h1>
            <div className={types.description}>
              Its an NFT assortment for builders in Crypto.
            </div>
            <div className={types.description}>
              {numberOfWhitelisted} have already joined the Whitelist
            </div>
            {renderButton()}
          </div>
          <div>
            <img className={types.picture} src="./crypto-devs.svg" />
          </div>
        </div>

        <footer className={types.footer}>
          Made with &#10084; by Crypto Devs
        </footer>
      </div>
    );
  }
Enter fullscreen mode

Exit fullscreen mode

  • Now create a brand new folder underneath the my-app folder and identify it constants.
  • Within the constants folder create a file, index.js and paste the next code.
  • Change "YOUR_WHITELIST_CONTRACT_ADDRESS" with the deal with of the whitelist contract that you just deployed.
  • Change "YOUR_ABI" with the ABI of your Whitelist Contract. To get the ABI in your contract, go to your hardhat-tutorial/artifacts/contracts/Whitelist.sol folder and out of your Whitelist.json file get the array marked underneath the "abi" key (it will likely be. an enormous array, near 100 strains if no more).
  export const abi = YOUR_ABI;
  export const WHITELIST_CONTRACT_ADDRESS = "YOUR_WHITELIST_CONTRACT_ADDRESS";
Enter fullscreen mode

Exit fullscreen mode

  • Now in your terminal which is pointing to my-app folder, execute
  npm run dev
Enter fullscreen mode

Exit fullscreen mode

Your whitelist dapp ought to now work with out errors πŸš€



Push to github

Be certain that earlier than continuing you’ve gotten pushed all your code to github πŸ™‚




Deploying your dApp

We are going to now deploy your dApp, so that everybody can see your web site and you’ll share it with your entire LearnWeb3 DAO buddies.

  • Go to Vercel and check in along with your GitHub
  • Then click on on New Mission button after which choose your Whitelist dApp repo
  • When configuring your new venture, Vercel will permit you to customise your Root Listing
  • Click on Edit subsequent to Root Listing and set it to my-app
  • Choose the Framework as Subsequent.js
  • Click on Deploy
  • Now you may see your deployed web site by going to your dashboard, choosing your venture, and copying the URL from there!

Share your web site in Discord πŸ˜€


Image description

This text is delivered to you by LearnWeb3 DAO. A free, complete A to Z blockchain coaching program for builders throughout the globe.

All the things from “What’s a Blockchain” to “Hacking sensible contracts”β€Š-β€Šand the whole lot in between, but additionally way more!
Be a part of us now to begin buidling with 25,000+ builders.

Website
Discord
Twitter



Add a Comment

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