DEV Community

loading...
Cover image for Ethereum Dapp Crash Course: Make an ERC20 token faucet + Frontend

Ethereum Dapp Crash Course: Make an ERC20 token faucet + Frontend

Richard Melkonian
working with solidity and react. composer of code / sound.
Updated on ・22 min read

Find all the source code for this project here Link

Ethereum smart contract development has been possible to learn since 2016 or so. But how to actually make a full stack Decentralised Application on the other hand, a different story.

This post was inspired by Nader Dabit's amazing post on eth development

a brief history of Dapp-development

Conflicting solidity versions, truffle, ganache, web3.js and testnet difficulties, with little written on frontend integration, or even basic design principles, made full-stack Dapp development a precarious endeavour. Whilst the tools above provided some amazing ways to get started in your dapp-dev journey (especially truffle!), two new members of the eth-dev metaverse have joined the party and made life much easier.

hardhat and ethers.js

...

I'd say that in the last 6 -12 months it's now actually fun and not that hard to get a full stack decentralised application up and running without having all manner of errors riddle your terminal. And any errors you may encounter, I've found easy to solve.

The Dapp development space is finding its feet and flourishing. We can expect this trend to strengthen and continue over the coming years.

If you like building bleeding edge tech, you're in the right place.

Before starting this crash course, I'd recommend having a good understanding of how ethereum works, smart contracts, transactions, gas fees and being familiar with javascript and react.

By the end of this tutorial you will have

  • a working, live decentralised application on a sharable url connected to the Goreli testnet
  • an understanding of the Dapp development framework and life cycle
  • an simple (but awesome) dapp
  • your mind blown

Let's get started!

Alt Text

our stack

react - our client side front end
react-bootstrap - fast css component styling
hardhat - ethereum / solidity development environment
ethers.js - ethereum / web client library

other tools

remix - an in-browser solidity development environment
metamask - our in-browser wallet which we will use to interact with our application
openzeppelin contracts a library of safe and audited smart contract solidity code
chai - a javascript assertion library for running tests
waffle - a library for smart contract testing

infura.io - An IPFS API that will connect our application to a live testnet
Goreli - The live testnet we'll be using
Github & Netlify - To host our client side UI

how they fit together

hardhat development envrionment

hardhat allows to run a local blockchain to test our application in an isolated environment. It allows us to compile, deploy and test our solidity smart contracts without using a live network. There are also a whole bunch of useful plugins made for hardhat.

ethers.js

a javascript library we'll be importing into our code. It allows our client side application to talk to the smart contracts that we'll be deploying onto the blockchain. It does this by generating JSON files containing ABI's which act as an interface between React and the smart contract.

ABI is an ethereum native term and stands for application binary interface. It will basically allow us to call the smart contracts functions.

It's important to mention here that the reason why we need ABI files is because ethereum uses the EVM, the Ethereum Virtual Machine, to read and write data to the blockchain via our smart contracts. Infact, when we compile our smart contract code, hard hat compiles it down to bytecode which is executed by the EVM. It's the lower level computation layer of the blockchain.

react

a javascript framework created by facebook that allows websites to render html elements live within one page, enabling complex single page sites like twitter to run fast. It's recommended you have some experience with react before starting this crash course!

We'll be using the ethers.js libary in-conjunction with react. Its a powerful combo!

Alt Text

react-bootstrap

a powerful library for react that allows us to use bootstrap in our react components. Using it also helps front end devs understand some useful design principles for react in general.

Part of the benefit of using react is that we can connect our application to the react ecosystem, pulling in other technology that we may want to use in an effortless way.

chai & waffle plugin

we'll be installing the hardhat waffle plugin which brings in a nice testing library

our Dapp - ERC20 token faucet

Wait, what are we making again? We're going to be making a simple sinlge page application that allows a user to receive 100 FCT, or faucet token.

Alt Text

Faucet's are useful for getting fake testnet ether in our wallets to use in the development. However what if we want a faucet for our own token?

If we're building any kind of Dapp, it might be useful to have a button that allows a user to receive some of our token, at least while we're still in development.

We'll be using the ERC20 token standard for our token.

We'll be adding one function to the smart contract called faucet() which will allow a user to receive 100 FCT.

The user will be able to :

  • Get 100 FCT
  • Check their FCT balance
  • Send FCT to another address (they can do this from within metamask wallet, but we'll be adding that functionality to our UI anyway)

let's get started

Prerequisites

  • Node.JS installed
  • Metamask wallet installed as an extension in your browser
  • You don't need any ETH at all to do this crash course.

setup and install dependencies

create a boiler plate react app

npx create-react-app react-token-faucet
Enter fullscreen mode Exit fullscreen mode

cd into your project directory and install hardhat and ethers.js. You can use NPM or Yarn. In the same line we're also adding the waffle plugins. During this install you may see some NPM warnings, don't worry about those.

npm install ethers hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers
Enter fullscreen mode Exit fullscreen mode

now we install the react-bootstrap library

npm install react-bootstrap bootstrap@4.6.0
Enter fullscreen mode Exit fullscreen mode

and finally we're going to install the Open Zeppelin Lirbary

npm install @openzeppelin/contracts
Enter fullscreen mode Exit fullscreen mode

open your project in your text editor of choice. You will see the default create-react-app folders.

In your package.json file you will see all the dependencies that we installed.

{
  "name": "react-token-faucet",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@nomiclabs/hardhat-ethers": "^2.0.2",
    "@nomiclabs/hardhat-waffle": "^2.0.1",
    "@openzeppelin/contracts": "^4.1.0",
    "@testing-library/jest-dom": "^5.12.0",
    "@testing-library/react": "^11.2.7",
    "@testing-library/user-event": "^12.8.3",
    "bootstrap": "^4.6.0",
    "chai": "^4.3.4",
    "ethereum-waffle": "^3.3.0",
    "ethers": "^5.2.0",
    "hardhat": "^2.3.0",
    "react": "^17.0.2",
    "react-bootstrap": "^1.6.0",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "web-vitals": "^1.1.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Enter fullscreen mode Exit fullscreen mode

in /src delete App.Tests.js, logo.svg and setupTests.js. We won't be using any of those files and we want to have a clean project.

Back in your terminal now run and initialise a hardhat project, enter through the initialisation.

npx hardhat run
Enter fullscreen mode Exit fullscreen mode

now add a .env file to your project

touch .env
Enter fullscreen mode Exit fullscreen mode

in your project root, you'll now see the hardhat folders and files added to our project, these are :

hardhat.config.js - project configuration
.gitignore - github should not push
/scripts/sample-script.js - our deployer script
/test/sample-test.js - tests

We need to edit our .gitignore file and our hardhat.config.js file.

.gitignore

This file contains a list of files that we do not want to push to github for security reasons.

Open this file and add .env under #misc

# misc
.env
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
Enter fullscreen mode Exit fullscreen mode

hardhat.config.js

this file contains all the information that hardhat needs to configure our project correctly.

change your hardhat config file to:

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

// 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 () => {
  const accounts = await 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 = {
  paths: {
    artifacts: './src/artifacts',
  },

  networks: {
    hardhat: {
      chainId: 1337
    },
  },
  solidity: "0.8.3"
};

Enter fullscreen mode Exit fullscreen mode

line 2 will require there to be a .env present.
The module.exports contains the bulk of information for hardhat to check.

artifacts is where our ABI's will be contained when we compile and deploy or solidity code.

networks contains information that hardhat needs to know about which network we are deploying our code to: ethereum mainnet, testnets or local network. For now, we are just going to be using our local network, hardhat. NB: The ChainId property needs to be set to 1337 to configure correctly with Metamask.

Finally change the filename of scripts/sample-script.js to scripts/deploy.js.

the faucet smart contract

Ok now we're going to code up the smart contract that we will be deploying to the blockchain and interacting with using our React front end.

Remix IDE

As discussed earlier, Remix is an in-browser solidity development environment. I've found it to be a great way to write smart contracts and test them before integrating them into my projects. Now that our project is set up, we're going to use Remix to test our smart contract. Using Remix IDE might seem a little long winded for this crash course, but I think it's important to cover. Let's check it out. Head over to

https://remix.ethereum.org/

In the left panel upon up Contracts, you'll see some example contracts. Create a new contract by clicking the new contract icon.

Alt Text

Create a new contract called FCTToken


pragma solidity ^0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";

contract FCTToken is ERC20 {

    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        _mint(msg.sender, 10000 * (10 ** 18));
    }

    function faucet (address recipient , uint amount) external {
      _mint(recipient, amount);
    }
}

Enter fullscreen mode Exit fullscreen mode

This is all the code for our smart contact! We are importing the Open Zeppelin library into our Remix IDE.

When we declare our Contract with Contract FCTToken is ERC20 our contract will inherit all the functions from the open zeppelin ERC20 token.

These functions are:

function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
function approve(address _spender, uint256 _value) public returns (bool success)
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
Enter fullscreen mode Exit fullscreen mode

This crash course wont be going into the details of solidity, but if you want to get a good understanding of ERC20 functionality, its worth checking out the Open Zeppelin repo and docs.

The constructor part of our code will initialise the smart contract parameters name and symbol with values that we will pass to it when it is deployed. These will be the name of our token and its symbol, "Faucet", "FCT".

Inside the constructor we call _mint (an inherited function) to mint 10,000 tokens. The math used when we call _mint has to be implemented because of token base units.

We added our new function to this smart contract faucet which takes two arguments *recipient of type address and ** amount of type uint.

NB. Solidity is a statically-typed language, if you're familiar with typescript it follows similar principles.

Now let's compile our code to test it out. Click the solidity icon in the furthest left panel.

Alt Text

Now click the compile button in the left panel.

Alt Text

If everything is working you'll see the Remix console run the compiler.

Now click the ethereum icon in the furthest left panel, to deploy our code in the remix environment.

Alt Text

We can see our contract instance awaiting deployment. Click deploy.

Alt Text

If all is working, you'll see a green tick in the Remix console.

Now we should see our smart contract instance under 'Deployed Contracts.' Click the arrow icon to expand it.

Alt Text

You'll now see a list of all the available functions we can use in our smart contract. Remember this includes all the functions we inherited from the Open Zeppelin Contracts import. (This really helped me visualise the smart contract functions when I was starting out with Solidity!)

Open up our faucet function.

Alt Text

You'll see the inputs for each argument, in our case recipient and amount.

Alt Text

At the top of the left panel you'll see a drop down menu under Account. This allows us to switch simulated user accounts, each account is loaded up with test ether. You should play around with Remix to learn solidity for sure. But for now we're going to copy the address of the current account by clicking the copy icon.

Alt Text

Now back in the faucet argument paste in the address for recipient and 100 for *amount and click transact

Alt Text

We ran a deployed a smart contract function! Now to check it worked, open your smart contract functions and call balanceOf with your address as the argument.

Alt Text

Before continuing it's worth noting the difference between the functions with orange buttons and the functions with blue buttons.

The orange functions write data to the blockchain, this counts as a transaction which costs gas. This action is immutable. The blue functions read data which counts as a call, this is free and doesn't change any data on the blockchain.

So now we know our smart contract is working, we can safely use it in our project.

In your project in the /contracts folder delete any contracts there and create a new contract called FCTToken.Sol with the following code, the code we just tested.

pragma solidity ^0.8.3;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract FCTToken is ERC20 {

    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        _mint(msg.sender, 100000 * (10 ** 18));
    }

    function faucet (address recipient , uint amount) external {
      _mint(recipient, amount);
    }
}

Enter fullscreen mode Exit fullscreen mode

Running our hardhat local blockchain

In your terminal, in your project directory run

npx hardhat node 
Enter fullscreen mode Exit fullscreen mode

You'll see hard had start running a local blockchain. It will give us a list of addresses loaded up with test eth. Leave this terminal window running and open a new one for the rest of this course.

Similar to how we deployed our smart contract in Remix, we are now going to deploy our FCTToken smart contract to our local blockchain.

Open you're scripts/deploy.js and update it to

const hre = require("hardhat");

async function main() {
  const [deployer] = await hre.ethers.getSigners();

  console.log(
    "Deploying contracts with the account:",
    deployer.address
  );

  const FCTToken = await hre.ethers.getContractFactory("FCTToken");
  const fctToken = await FCTToken.deploy("FCTToken", "TKN");

  await fctToken.deployed();

  console.log("Token deployed to:", fctToken.address);
}

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

Now we've updated our deploy script, we can compile and deploy the FCTtoken.sol

N.B Compiler won't accept .Sol, file must be name .sol, lowercase S.

In a new terminal window, cd into your project and run

npx hardhat compile

Then -

npx hardhat run scripts/deploy.js --network localhost
Enter fullscreen mode Exit fullscreen mode

Terminal should print out something similar to

Alt Text

N.B Human users have addresses for their wallets, but so do smart contracts. Addresses on ethereum can be thought of as id's for both users/wallets and also smart contracts.

Take note of the Token Deployed To address as we'll be using that later.

Cool! So we've deployed our FCTtoken.sol contract to our local hardhat blockchain.

If you're still with me, now would be a good time for that 10 minute break ;)

React Frontend

We can interact with the contract straight from our terminal using lots of nice hardhat commands. But for this crash course, we're going to get straight into react.

Our aim is to have a webpage with a few buttons. These buttons will call our smart contract functions. We want:

  • faucet button send FCT to user wallet
  • check balance display a message with current FCT user balance
  • send FCT user can send FCT to another address
  • amount input an input for the amount to send
  • address input an input for the address of the payee

In our /src/app.css file, delete all the default code and replace it with:


.App {
  text-align: center;
  background-color: rgba(252, 203, 250, 0.65);
  height: 100%;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.App-header {
  background-color: rgb(253, 204, 251);
  padding: 20px;
  color: white;
}

.App-intro {
  font-size: large;
}

Enter fullscreen mode Exit fullscreen mode

Let's clean up our App.js file. Let's start with

import './App.css';
import FCTToken from './artifacts/contracts/FCTToken.sol/FCTToken.json'

function App() {

  const Token = FCTToken;

  return (
    <div className="App">
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

now in your terminal run to start your localhost

npm run start 
Enter fullscreen mode Exit fullscreen mode

this will load up at localhost:3000 our blank page with a lovely light, purple-pink.

Alt Text

yum

Now let's build out the App.js component a little more.

import './App.css';
import FCTToken from './artifacts/contracts/FCTToken.sol/FCTToken.json'
import 'bootstrap/dist/css/bootstrap.min.css'
import { Container, Row, Col } from 'react-bootstrap'

function App() {

  const Token = FCTToken;

  return (
    <div className="App">
    <Container>
    <Row className="justify-content-md-center">
      <Col>
      <div>our faucet</div>
      </Col>
      <Col>
      <div> our send area</div>
      </Col>
    </Row>
    </Container>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

We imported some bootstrap-react components, and have just sketched out where we want our main UI components to be on the page.

Check out the beautiful bootstrap-react docs page on layout if you've never implemented them in react before.

You should see the page reload every time you make a change / save a change to your react code.

Lets create our Faucet component in our code by adding this snippet at line 22

 <Faucet  tokenContract={Token}/>
Enter fullscreen mode Exit fullscreen mode

This creates a react component and sends it the prop Token. This variable contains the ABI (remember those?) which we're importing at line 5.

Now lets code up the Faucet Component.

in your terminal run

cd src 
mkdir components 
cd components 
touch Faucet.js 
Enter fullscreen mode Exit fullscreen mode

to create the Faucet.js file. You should have this file structure for react components.

Alt Text

Here's the code for our Faucet.js react component.


import { useState } from 'react';
import { ethers } from 'ethers'
import Card from 'react-bootstrap/Card'
import Button from 'react-bootstrap/Button'

const tokenAddress = "{YOUR DEPLOYED TOKEN ADDRESS}"

const Faucet = (props) => {

  const [balance, setBalance] = useState()
  const [showBalance, setShowBalance] = useState(false)


  async function getBalance() {
    if (typeof window.ethereum !== 'undefined') {
      const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' })
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const contract = new ethers.Contract(tokenAddress, props.tokenContract.abi, provider)
      const balance = await contract.balanceOf(account);
      console.log("Balance: ", balance.toString());
      setBalance(balance.toString());
      setShowBalance(true);
    }
  }

  async function faucet() {
    if (typeof window.ethereum !== 'undefined') {
      const account = await window.ethereum.request({ method: 'eth_requestAccounts' });
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const contract = new ethers.Contract(tokenAddress, props.tokenContract.abi, signer);
      contract.faucet(account[0], 100);
    }
  }
    return (
        <div>
        <Card style={{background: "rgba(227, 104, 222, 0.71)"}}>
        <Card.Body>
        <Card.Subtitle>recieve faucet ERC20 to your wallet
        </Card.Subtitle><br></br>
        <div className="d-grid gap-2">
        <Button onClick={faucet}>get faucet token!</Button>
        <Button onClick={getBalance} variant="warning">check my balance</Button>   
        </div>
        </Card.Body>
        </Card>
        </div>
    )
}

export default Faucet
Enter fullscreen mode Exit fullscreen mode

On line 7, you'll need to replace the tokenAddress value with the 'Token Deployed To' address we got from our terminal earlier on.

The code above might look a little clunky, but it's actually pretty simple once we break it down. We've declared two async functions getBalance() and faucet(). These need to be asynchronous functions as we are calling the smart contract which lives in the block chain so we need to Javascript to be patient with ethereum!

Read up on asynchronous programming in Javascript if it's unfamiliar to you as you'll be doing it a lot in Dapp development.

In our function we check if the user has metamask running, then we get the data we need using the ethers.js library and assign that data to local variables. Again, this data is made accessible via the ABI, which we're importing at App.js and passing in as a prop to Faucet.js.

The beauty of react design principles, flowing with ethereum ABI's!

Phew!

Ok, now we need to import our lovely component in to our App.js at the top of the file.

import Faucet from './components/Faucet.js'
Enter fullscreen mode Exit fullscreen mode

Back on your localhost:3000 you should see something like this ...

Alt Text

The pink background around our Faucet component happens because we wrapped it in a react-bootstrap component which we customised inline with some CSS styling.

The different colour buttons are set using the variant prop, you can read about how they work here.

Testing the faucet component

Let's play with our UI. First we need to setup our Metamask Wallet so that its connected to our hardhat blockchain node

'...not another setup,' I hear you screaming in the void...

When we ran our hardhat blockchain node, the terminal gave us a list of addresses and private keys for testing. Your node should still be running. Scroll up until you see something like this:

Alt Text

We can use any of these accounts to connect to our Metamask to our local blockchain instance. Use the first one for the sake of clarity. Copy the private key of the first account in the list.

Open Metamask and select the available networks dropdown. Connect to localhost:8545, this is the port of our local blockchain

Alt Text

Now go to Import Account.

Alt Text

Paste in the private key of the first account from your terminal. Once the account is connected, you'll see your account loaded up with fake test eth!

In our UI click get balance

Open your browser developer console and you should see this console log.

Alt Text

This is the our wallet balance of FCT token. We minted the contract and in our smart contract, the minter receives the whole supply.

In our UI lets click get faucet token!

Alt Text

Metamask will ask you to connect to the site. We will then be shown this transaction to confirm in our metamask wallet.

Alt Text

This shows the gas fee to run the transaction. Obviously we are just using test eth, but on the mainnet, this would cost actual eth.

Click confirm.

Remember that every time we interact with a smart contract, if we are changing data stored in the blockchain that counts as a transaction.

Now if you click get balance. You should see the updated balance.

Alt Text

You can see our balance has updated by 100 Wei.

Getting the balance of the user address as a read only call, which doesn't count as a transaction, so requires no gas to run.

Let's turn that console.log into a message so our UI updates dynamically.

Create a new file Message.js in your components folder.

Alt Text

The code for our Message react component.

import Alert from 'react-bootstrap/Alert'

const Message = ({ balance }) => {
    return (
      <div>
      <Alert variant="info"> balance : {balance}</Alert>
      </div>
  )
}

export default Message
Enter fullscreen mode Exit fullscreen mode

we're importing the Alert component from bootstrap. And passing in the balance prop.

Back in your Faucet.js file add the following code, in my file, at line 45. Right under your get Balance button

{ showBalance ? <Message balance={balance}/> : null }
Enter fullscreen mode Exit fullscreen mode

And import the component into the Faucet.js file at the top of your code

import Message from './Message'
Enter fullscreen mode Exit fullscreen mode

Now if we click get balance in our UI, we will see the message component render to our page.

Alt Text

Now let's create our send token component

In our App.js file the new component where we have the dummy text "our send area"

<TokenSend tokenContract={Token}/>
Enter fullscreen mode Exit fullscreen mode

Again we're passing our TokenABI as a prop to this component.

Create a new file in your components folder named TokenSend.js

Add the following code.


import { useState } from 'react';
import { ethers } from 'ethers'
import Card from 'react-bootstrap/Card'
import Button from 'react-bootstrap/Button'

const tokenAddress = "{YOUR DEPLOYED TOKEN ADDRESS}"

const TokenSend = (props) => {

  const [userAccount, setUserAccount] = useState()
  const [amount, setAmount] = useState()

  // request access to the user's MetaMask account
  async function requestAccount() {
    await window.ethereum.request({ method: 'eth_requestAccounts' });
  }

  async function sendCoins() {
  if (typeof window.ethereum !== 'undefined') {
    await requestAccount()
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(tokenAddress, props.tokenContract.abi, signer);
    const transation = await contract.transfer(userAccount, amount);
    await transation.wait();
    console.log(`${amount} Coins successfully sent to ${userAccount}`);
  }
}
    return (
        <Card style={{background: "rgba(227, 104, 222, 0.71)"}}>
        <Card.Body>
        <Card.Subtitle> send faucet to an address
        </Card.Subtitle>
        <br></br>
        <div className="d-grid gap-2">
        <input onChange={e => setUserAccount(e.target.value)} placeholder="Payee 0x address" />
        <input onChange={e => setAmount(e.target.value)} placeholder="Amount" />
        <Button onClick={sendCoins} variant="success">send </Button>
        </div>
        </Card.Body>
        </Card>
    )
}

export default TokenSend
Enter fullscreen mode Exit fullscreen mode

Update the variable tokenAddress with your deployed contract address.

And finally back in your App.js file import the TokenSend.js component.

import TokenSend from './components/TokenSend.js'
Enter fullscreen mode Exit fullscreen mode

You will now be able to send tokens to different wallets/users.

In amount enter 1000000000000000000 (equal to one whole token in wei), then add an address from on of the addresses supplied to us by hardhat.

Click send. Confirm the transaction in Metamask in our browser terminal we should see the console.log:

Alt Text

You simulate multiple users by importing different accounts into your metamask and switching between them to test the functionality.

Well Done

We now have a working Decentralised Application. Our front end is running on localhost:3000 and our local hardhat blockchain on localhost:8545.

Now we're going to test it our on real network - real miners will process our transactions!

Preparing for live testnet

We're going to use the Goreli testnet to deploy our smart contract.

You'll need to load up your wallet with test eth for the Goreli network, or GOeth.

First, connect your Metamask wallet to the Goreli testnet.

Alt Text

I used this one, but there are others if you have a google.

To connect to Goreli, we'll need to make use of an API, Infura.io has us covered, and its fast to set up.

Set up a free account and sign in. Go into dashboard and click the ethereum icon in the left panel.

Now click the create new project button in the top right of the page.

Alt Text

Name your project.

In your project settings set the endpoints dropdown box to Goreli

Now you want to copy and save somewhere your

  • ProjectID
  • end point URL

That's all we need from Infura.

Change Configuration

Open your hardhat.config.js file and update it to

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

// 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 () => {
  const accounts = await 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 = {
  paths: {
    artifacts: './src/artifacts',
  },
  networks: {
    hardhat: {
      chainId: 1337
    },
    goreli: {
      url: "{YOUR END POINT URL}",
      accounts: [`0x${process.env.PRIVATE_KEY}`]

    }
  },
  solidity: "0.8.3"
};
Enter fullscreen mode Exit fullscreen mode

Update the gorlei.url property to be the endpoint URL that we saved from Infura, (no curly braces, just a string).

In goreli.accounts we are referencing the variable PRIVATE_KEY stored in our .env file. This file won't be pushed to github as its stored in our .gitignore file.

To get the private key, open your metamask, make sure you're in the account with the Goreli test eth.

Click the three button icon, and go to Account Details. In the next pop up click export Private Key.

You should have different metamask accounts for developing and anything with real crypto assets.

Alt Text

In your .env file update your private key.

PRIVATE_KEY="{YOUR-PRIVATE-KEY}"
Enter fullscreen mode Exit fullscreen mode

Never expose your private key in your config file, if you do anyone can access your wallet and steal your funds

By referencing the secret variable in our config file, our private keys are safe.

Deploy!

In your terminal run

npx hardhat run scripts/deploy.js --network goreli
Enter fullscreen mode Exit fullscreen mode

If all is working you'll see something like

Alt Text

If you search the deployed contract address on the Goreli etherscan, you'll see it live on the blockchain. In etherscan you can see useful information about the contract, its total supply and even its original source code.

Before we can test out our UI we need to update two lines in our react code.

In Faucet.js, line 7.

const tokenAddress = "{YOUR-DEPLOYED-TOKEN-ADDRESS}"
Enter fullscreen mode Exit fullscreen mode

And in Token.js, the same local variable name needs the same address.

Now our UI works. When we click get faucet token or send we call a function to our smart contract on the Goreli testnet!

You may have to wait a little while for the transaction to be mined on Goreli, but you can check the status of your transaction on the Goreli etherscan as you would any normal transaction.

Pretty Cool!

Hosting Our UI

Wouldn't it be nice if we could share our Dapp with a single URL?

It would. And that's what we're going to do.

First, make a new repo and push your project to the master branch. I won't be going into how to do that here. Our .gitignore file is configured so that we will only push our source code to Github.

Once you've pushed to your new Git repo. Go to netlify, create a new account or log in.

Click new site from git button. Connect Netlify with your Github account and select the repo you just created, to deploy your UI.

Netlify will count any react compiler warnings and errors, so make sure there are no warnings in your react project before you deploy.

That's it! Once its deployed Netlify will supply you with a URL for your Dapp, connected to the Goreli test net, so you can share your work.

Here's mine

Summary

In this crash course I tried to show what the full development cycle of a decentralised application might look like. Note we didn't do any testing with Mocha on this project and that's a topic for another day.

Feel free to share and adapt, create your own UI's, add custom smart contract functionality with UI capabilities. I'd love to see your work.

I'm an eth/solidity dev ready hire specialising in front end integration and UI. Let's chat.

Discussion (11)

Collapse
tssandor profile image
TSS • Edited

Great stuff!

There is one important step missing before deploying the contract to the local blockchain: compiling it! You need an npx hardhat compile. It took me AGES to figure out why it refused to compile the file. Turned out that it couldn't recognize .Sol, only .sol - maybe useful for someone struggling with the same :)

Collapse
richardmelko profile image
Richard Melkonian Author

Thank you so much for this, I must have missed this command, I'm going to edit the post now. Well done for figuring it out. And glad you got something from this.

Collapse
senorgeno profile image
Gene

Thanks for this!

You might want to also change the name here too
'.Sol' - "In your project in the /contracts folder delete any contracts there and create a new contract called FCTToken.Sol with the following code, the code we just tested."

Thread Thread
richardmelko profile image
Richard Melkonian Author

will update, thanks

Collapse
samsafi profile image
Sam-Safi

Hi Richard,
First of all thank you for this crash course.
I followed your code and when I click on “check my balance” button I got this error

Unhandled Rejection (Error): network does not support ENS (operation="ENS", network="unknown", code=UNSUPPORTED_OPERATION, version=providers/5.3.0)

Collapse
drdarian profile image
drdarian

I had the same issue. I fixed it by changing the 'tokenAddress' variable in Faucet.js.

I had originally put in the address for my Metamask wallet which caused the error. You should use the contract address given to you by your Hardhat terminal which you should receive after you call 'npx hardhat node'. It will be located after your hardhat_addCompilationResult

Collapse
adouch9 profile image
adouch9

Me too I have the same problem

Collapse
senorgeno profile image
Gene

Hey, I am getting "Error: Expected private key to be an Uint8Array with length 32" when trying to deploy to Goreli. Any ideas?

Collapse
klaudioz profile image
Claudio Canales

You're using Account #0 address instead the Private Key address.

Collapse
richardmelko profile image
Richard Melkonian Author

not sure.. make sure your storing your private key as the correct variable type in your .env file

Collapse
mrifai profile image
Muhamad Rifai

when the faucet button claims, the total max supply keeps increasing, why is that?