Introductory fullstack ethereum dapp using: solidity, hardhat, react.js, ethers.js

Overview

Intro to Fullstack Ethereum Development

Check out deployed dapp here! (Make sure you're connected to the Rinkeby Testnet)

This article will help you escape writing solidity tutorials in Remix and explain the tools you will need to create a simple full-stack dapp. The smart contract will be very simple itself and that is because we're focusing on all of the other tools you will need.

Discord Link for help

Our stack

  • Solidity (To write our smart contract)
  • Hardat (build, test and deployment framework)
  • React (Create our frontend)
  • Metamask (Web Wallet that will allow us to interact with the ethereum blockchain)
  • Ethers (web3 library for interacting with the blockchain and our smart contract)

Installing a web wallet

Before getting started make sure you have a web wallet installed, I recommend Metamask. It is essentially just a browser extension that will allow us to interact with the ethereum blockchain. Just follow the instructions provided in the link to install and make sure not to use the same seed phrase for development for real ethereum/money.

Environment

First head over to the hardhat website, we're going to be doing most of what is covered in the tutorial section as well as some of the documentation.

Make sure you have nodejs installed, if you don't then follow the setup here

Create a new project

To get your project started:

mkdir intro-fullstack-ethereum
cd intro-fullstack-ethereum
npm init --yes
npm i --save-dev hardhat

In the same directory where you installed Hardhat run:

npx hardhat

A menu will appear, for this tutorial we will be selecting Create an empty hardhat.config.js

Make sure these packages are installed, you may have been asked to install them when initializing the project.

npm install --save-dev @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai

Create our Contract

Let's create our contract, it will be very simple, the contract will only read from and write to the blockchain. I wanted to keep the contract simple since this will be a comprehensive look at all of the tools necessary to create a fullstack dapp.

First we will need to create a directory for our contract, hardhat expects them to be in a directory called contracts/ so:

mkdir contracts
cd contracts
touch SimpleStorage.sol

Our Smart Contract

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.9.0;

contract SimpleStorage {
    uint256 storedData;

    constructor(uint256 _storedData) {
        storedData = _storedData;
    }

    function set(uint256 x) public {
        storedData = x;
    }

    function get() public view returns (uint256) {
        return storedData;
    }
}

This contract is very simple. When the contract is deployed it is instantiated with a value called storedData, after deploying the contract you have the ability to get the data or set the data.

Compiling our contract

We can now use hardhat to compile our contract with

npx hardhat compile

Shorthand commands

Instead of typing out npx hardhat <command> we can install hardhat-shorthand and use the hh command

npm i -g hardhat-shorthand

We can also get tab completion by running the command:

hardhat-completion install

Choose to install the autocompletion for your shell and you should now get tab completion after typing hh as long as you are in a hardhat project directory.

Try compiling your contract now with:

hh compile

Testing

It is very import since money is often on the line when it comes to smart contracts. I will show you how to create a simple test for our SimpleStorage contract.

It is also important to note that hardhat comes with it's own network so when we run our tests hardhat will spin up a local network where we can deploy our contract to and test.

First create a directory called test/ and create a file called simple-storage-test.js

mkdir test
touch simple-storage-test.js

Here are our simple tests:

const { expect } = require('chai')

describe('SimpleStorage contract', function () {
  it('test deployment', async function () {
    const SimpleStorage = await ethers.getContractFactory('SimpleStorage')

    const simpleStorage = await SimpleStorage.deploy(123)

    const storedValue = await simpleStorage.get()

    expect(storedValue).to.equal(123)
  })

  it('test set new value', async function () {
    const SimpleStorage = await ethers.getContractFactory('SimpleStorage')

    const simpleStorage = await SimpleStorage.deploy(123)

    await simpleStorage.set(456)

    const storedValue = await simpleStorage.get()

    expect(storedValue).to.equal(456)
  })
})

You will always be able to call the deploy method on your contract even if you didn't define a method called deploy essentially it just calls your constructor.

We have defined two tests here one just deploys the contract with an initial value and checks that it was deployed properly, the other does the same except we set a new value using the set method defined in our smart contract.

Before we can run our test we will need to require hardhat-waffle this will make the ethers variable available in global scope

So add the following line to the top of your hardhat.config.js file:

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

Ok now we are ready to run our test:

$ hh test # or npx hardhat test

  SimpleStorage contract
    ✓ test deployment (366ms)
    ✓ test set new value (52ms)


  2 passing (420ms)

You will notice the text that we added to the test is printed out when the test runs

NOTE Another good example test: link

console.log in solidity

When running contracts inside of the hardhat network we can make use of a special logging function provided by hardhat, here is an example of how to add it to your contract:

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.9.0;

import "hardhat/console.sol";

contract SimpleStorage {
    uint256 storedData;

    constructor(uint256 _storedData) {
        console.log("Deployed by: ", msg.sender);
        console.log("Deployed with value: %s", _storedData);
        storedData = _storedData;
    }

    function set(uint256 x) public {
        console.log("Set value to: %s", x);
        storedData = x;
    }

    function get() public view returns (uint256) {
        console.log("Retrieved value: %s", storedData);
        return storedData;
    }
}

Now when we run hh test we should see:

  SimpleStorage contract
Deployed by:  0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
Deployed with value: 123
Retrieved value: 123
    ✓ test deployment (465ms)
Deployed by:  0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
Deployed with value: 123
Set value to: 456
Retrieved value: 456
    ✓ test set new value (74ms)

As you can see the logging function is very useful when trying to figure out what is happening with the internal logic of the smart contract.

Deploying our contract

Now we are ready to deploy our contract, hardhat provides a local blockchain for testing so that we don't need to setup a node or use a third party provider to deploy to a testnet. We will first deploy to the local network and then optionally I will show you how to deploy to a testnet.

Deployment script

Before we can deploy our contract we will need a deployment script so that hardhat knows how to instantiate the contract.

  • First create a directory called scripts/ and a file inside called deploy.js:
mkdir scripts/
touch scripts/deploy.js
  • Here is some example code we can use to deploy our contract:
async function main() {
  // We get the contract to deploy
  const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
  const greeter = await SimpleStorage.deploy(789);

  // NOTE: All Contracts have an associated address
  console.log("SimpleStorage deployed to:", greeter.address);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

Local deployment

  • Start a local node
hh node # or npx hardhat node

You should see that 20 accounts are created with 10000 ETH each.

  • In another terminal deploy the contract:
npx hardhat run --network localhost scripts/deploy.js

In the terminal where you started your local node you should have noticed the following output:

  Contract deployment: SimpleStorage
  Contract address:    0x5fbdb2315678afecb367f032d93f642f64180aa3
  Transaction:         0x024bf3c1eb46ed3f405b7b10ea4c5e6b46c9e9deeed38d0743c6a7ae16b4d5b1
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  Value:               0 ETH
  Gas used:            290646 of 290646
  Block #1:            0x0eabbc7dff1b963c76b5b077d3df3353f201a450d72361b3de3218454cffe105

  console.log:
    Deployed by:  0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
    Deployed with value: 789

NOTE Notice the console.log section, we can utilize the builtin logging functionality whenever our contract is deployed on the hardhat network, this is not just available in testing.

NOTE The Contract address will be useful later when we want to interact with our contract

Creating our frontend

We will be using React since it is by far the most popular framework used to create frontends for dapps.

If you need to brush up on your React skills I recommend checking out this tutorial: React Tutorial

Make sure you are in the root of our project (in the intro-fullstack-ethereum directory) and run:

npx create-react-app frontend

Make sure you can start your frontend by entering the frontend directory and running:

npm start

Accessing our contract

Before we get started writing any code for the frontend we will need to have access to our contracts ABI or Application Binary Interface, which is basically just a JSON file containing the functions, permissions and other information about our smart contract. Normally this would be found under the ./artifacts/contracts/SimpleStorage.sol directory and be called: SimpleStorage.json, but we want to move this somewhere that our frontend will be able to access it. so that ethers.js will know what functions are available for example.

Instead of moving this file somewhere our frontend can find it. I think it would be better if every time we compile our contract the file is automatically placed in frontend/src/artifacts. We can achieve this by adding the paths option to our hardhat.config.js file:

module.exports = {
  solidity: '0.8.4',
  paths: {
    artifacts: './frontend/src/artifacts',
  },
}

Now whenever we run hh compile the ABI is placed in ./frontend/src/artifacts

Metamask Hardhat Local Blockchain Fix

Before we can use metamask with our local blockchain we also need to add the following to hardhat.config.js:

module.exports = {
  solidity: '0.8.4',
  paths: {
    artifacts: './frontend/src/artifacts',
  },
  networks: {
    hardhat: {
      chainId: 1337,
    },
  },
}

Metamask chainId issue

Interacting with the Blockchain (using ethers.js)

Ok so now that we've created our smart contract, tested it, deployed it, and obtained the ABI, we are now ready to interact with our contract and create our dapp.

To begin remove all of the code in src/App.js and replace it with the code found at the following link: App.js

Instead of explaining all of the code here, the file is heavily commented and should explain everything you need to know.

Also get the deployed contract address from earlier and set in App.js:

const simpleStorageAddress = '0x5fbdb2315678afecb367f032d93f642f64180aa3'

Connect Metamask to Local Blockchain

Open up your Metamask extension, click on the top where it says mainnet and choose Localhost 8545.

I also recommend importing one of the accounts into Metamask so that you have 10,000 ether to play with. You can import by private key in metamask so just grab one of the private keys from the terminal where you started your node.

Interact with the Dapp

You should now be able to interact with the dapp. Try getting the value and observe that it is the same value passed to the deploy function in out deploy script. After clicking the connect button you will be able to spend some ether and update the value in the smart contract.

NOTE: Remember changing values on chain cost ether, reading values from the blockchain is free.

Styling

If you want to be a fullstack blockchain developer then you cannot escape learning css and how to create a solid UI/UX for your users, or investors. There are solutions like bootstrap available, but I would recommend at least knowing the basics including flexbox, css-grid and how to make your site responsive.

Here are some good resources to learn css

Remove all of the code in src/App.css and replace it with the code found at the following link: App.css

A basic understanding of css and flexbox is all you will need to understand the code found at that link. For frontend styling idea/inspiration I recommend heading over to a site like coingecko, and click on a token you're interested, for most of them you should see a site associated with the token where you can checkout their dapp for instance here is a link to uniswap

Testnet deployment (Optional)

In order to deploy your contract to a testnet you will need to edit the hardhat.config.js to include networks other than localhost.

You will need to set up an account with a node provider (you could do this without one but it will be much more complicated) for this tutorial I set one up at alchemy.io

We'll also need to install another package called dotenv so we can get secrets like the private key and node url from environment variables, that way you don't need to worry about committing them to source control.

cd frontend

npm i dotenv

For the environment variable: RINKEBY_PRIVATE_KEY replace it with your Rinkeby account private key. To export your private key from Metamask, open Metamask and go to Account Details > Export Private Key.

WARNING: Don't use the private associated with an account you keep any real ether in!

For the environment variable: RINKEBY_URL replace it with the http key after setting up a rinkeby app in alchemy.

# Replace with your values
export RINKEBY_URL=https://eth-rinkeby.alchemyapi.io/v2/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export RINKEBY_PRIVATE_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Here is an example of adding the Rinkeby testnet to our list of networks:

require('@nomiclabs/hardhat-waffle')
require("dotenv").config();

// Replace this with a URL generated after setting up and account
// with a node provider e.g. alchemy.io
const RINKEBY_URL = process.env.RINKEBY_URL

const RINKEBY_PRIVATE_KEY = process.env.RINKEBY_PRIVATE_KEY

module.exports = {
  solidity: '0.8.4',
  paths: {
    artifacts: './frontend/src/artifacts',
  },
  networks: {
    hardhat: {
      chainId: 1337,
    },
    rinkeby: {
      url: `${RINKEBY_URL}`,
      accounts: [`${RINKEBY_PRIVATE_KEY}`],
    },
  },
}

After adding the new entry you will need to deploy your contract using the same deployment script and command as before

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

After running this command copy the address for your deployed contract and head over to rinkeby.etherscan.io and click on the Contract tab and Verify and Publish your contract. For this tutorial you can select Solidity (Single file) for Compile Type, 0.8.4 for the Compiler Version and MIT for License Type

You will also need to update the contract address in App.js, example:

const simpleStorageAddress = '0xde4De608C284709E8980212C7A48B8bcA5b570A2'

Get Test Ether

Make sure you're connected to the Rinkeby Network in Metamask before you get started. After that all you will need to do is paste your public address into the form, solve a captcha and get your ether.

Rinkeby Faucet

Interact with Contract Deployed on Testnet

You should now be able to interact with the contract deployed to the rinkeby testnet in the same way you did with the local deployment.

Deploy to Netlify

Finally we can deploy our dapp to a hosting site like Netlify.

To get started head over to netlify.com and create an account.

You will also need to install netlify-cli via npm

npm i -g netlify-cli

Now we can build our application and deploy.

cd frontend/

npm run build

netlify deploy

Follow the command line prompts and choose yes for a new project and ./build as your deploy folder.

You might also like...

Minimal Typescript / NextJS dApp template bootstrapped with wagmi Ethereum react hooks library.

Welcome to the NextJS wagmi starter template 👋 Looking to get up and running with a Typescript / NextJS dApp as quickly as possible? You're in the ri

Jan 4, 2023

An NFT Marketplace built with NextJS, Hardhat and Solidity

An NFT Marketplace built with NextJS, Hardhat and Solidity

🖼️ NFT Marketplace This is a fullstack DApp NFT Marketplace built as a study project to learn more about blockchain and smart contract development. M

Dec 31, 2022

Solidity starter combining foundry and hardhat because both are great and I can't live without either...

Combination Pizza Hut & Taco Bell Foundry && HardHat starter template. Motivation I like them both. With this set-up we get: Unit tests written in sol

Aug 23, 2022

Kittos is NFT Marketplace built with Next Js, Hardhat, Solidity, Arweave + Bundlr Client and All The CSS Magic with TailwindCSS. 😺

Kittos is NFT Marketplace built with Next Js, Hardhat, Solidity, Arweave + Bundlr Client and All The CSS Magic with TailwindCSS. 😺

Kittos NFT Marketplace 😺 Built with Next Js, Hardhat, Solidity, Arweave, Bundlr and Tailwind CSS. Functionalities New Listed Assets Mint NFT Buy NFT

Dec 24, 2022

Elven Tools Dapp - Elrond blockckchain frontend dapp demo. Primarily for NFT minting, but it can be used for other purposes.

Elven Tools Dapp Docs: elven.tools/docs/landing-page.html Demo: dapp-demo.elven.tools Sneak peek: youtu.be/ATSxD3mD4dc The Dapp is built using Nextjs

Jan 1, 2023

A MERN Stack dapp the utilizes three solidity contracts

A MERN Stack dapp the utilizes three solidity contracts. It verifies user ownership of third party NFTs, generates an image incorporating a third party NFT image pulled from IPFS, mints a new NFT for users that includes an on-chain message and metadata to Opensea standards.

Jun 30, 2022

A professional truffle solidity template with all necessary libraries that support developer to develop, debug, test, deploy solidity smart contract

solidity-truffle-template A professional truffle solidity template with necessary libraries that support to develop, compile, test, deploy, upgrade, v

Nov 4, 2022

🌱 Ethereum provider solution for Dapp&Wallets, 🏷 If you have good suggestions, please submit issues

🌱 Ethereum provider solution for Dapp&Wallets,  🏷 If you have good suggestions, please submit issues

English | 简体中文 | 日本 ETH Wallet Modal An Ethereum Provider Solution for Integrated Wallets and Dapps ⚠️ Notice If you need to reduce unnecessary import

Dec 19, 2022

🏦 Defi Bank is a dapp created for ethereum 101 course of cadena.dev

🏦 Defi Bank is a dapp created for ethereum 101 course of cadena.dev

Welcome to DefiBank 👋 Defi Bank is a dapp created for ethereum 101 course of cadena.dev ✨ Demo Install npm install Usage npm run dev Tech NextJS Reac

Nov 21, 2022
Owner
Christian Chiarulli
Fullstack Software Developer
Christian Chiarulli
Build a Full Stack Marketplace on Ethereum with React, Solidity, Hardhat, and Ethers.js

Building a Digital Marketplace on Ethereum The technologies used in this workshop are React, Next.js, Tailwind CSS, HardHat, Solidity, and Ethers. Get

Nader Dabit 114 Nov 15, 2022
Decentralized twitter using Solidity, Ethereum, hardhat, ethers, IPFS, Next.JS, TypeScript, TailwindCSS.

DWITTER: Decentralized Twitter Check out the deployed version of this app at https://dwtr.wajeshubham.in Transactions on Ethereum are slow. Therefore,

Shubham Waje 12 Sep 2, 2022
A hardhat solidity template with necessary libraries that support to develop, compile, test, deploy, upgrade, verify solidity smart contract

solidity-hardhat-template A solidity hardhat template with necessary libraries that support to develop, compile, test, deploy, upgrade, verify solidit

ChimGoKien 4 Oct 16, 2022
Using a Decentralized Application (DApp) to Sell artwork on the Ethereum blockchain with smart contracts written in Solidity.

Decentralized Applications For Selling Limited Time Artwork This repository houses the Solidity, JavaScript, and HTML code for a Decentralized Applica

Keyan Ahmadi 4 Mar 20, 2023
A web3 starter project using Typescript, Hardhat, ethers.js and @web3-react

Starter React Typescript Ethers.js Hardhat Project This repo contains a Hardhat and React Dapp starter project. The React Dapp in the frontend dir of

ChainShot 39 Dec 31, 2022
Solidity Quickstart is an extensive solidity guide for the solidity newbies out there.

?? Solidity Quickstart Solidity Quickstart is an extensive solidity guide for the solidity newbies out there. ?? How does it work? All the guides rela

Kira 8 Aug 6, 2022
This is a fully functional DAO, a Web3 project made using Solidity, Hardhat, JS, Next.js, Openzeppelin, CSS, HTML, using the Rinkerby network!

My First DAO! This is made for a DAO-Tutorial via learnweb3.io This is a DAO, a decentralised autonomous organisation, essentially a more collective a

Kell (K42) 3 Jun 20, 2022
Foundry-Hardhat plugins: Use Foundry for Hardhat projects

This repo contains hardhat plugins to use foundry tools in hardhat environments. Installation See in each plugin anvil forge foundryup Documentation F

Foundry 77 Nov 3, 2022
A comprehensive collection of useful tools developed with the help of Ethers.js to interact with the Ethereum Blockchain to develop great DeFi apps as quickly and easily as possible.

hudi-packages-ethersfactory How to install Installing with npm For more information on using npm check out the docs here. npm i @humandataincome/ether

HUDI 6 Mar 30, 2022
Tip Tweet is a hybrid dApp that provides a simple way to tip a tweet using Ethereum. Authors can claim their tips using their Twitter account. You only need the tweet URL to tip. 🚀 😎

Tip Tweet Table of Contents About Folder Structure Contract Deveopment Starting the App Usage Contributing About Tip Tweet is hybrid dApp that allows

Dias Junior 23 Nov 15, 2022