External Plugin

This is a third-party plugin. Please report issues in its Github Repository(opens new window)

hardhat(opens new window)

# Part of contribution to the ETHOnline hackathon

Alpha release, interfaces will change.

# Hardhat React

A Hardhat plugin that generates a React hook component from your smart contracts. Hot reloaded into your React app. Deployed or not deployed. And everything typed and initialized.

  • Uses deployments from hardhat-deploy to inject the latest instance of your smart contracts into the React app.
  • Uses Typechain so all your smart contracts are typed.
  • Uses Ethers so everything else is typed.
  • Runs alongside hardhat-node or hardhat-deploy --watch so any change you do in a smart contract is immediately injected into React app. Just start the hardhat runtime, and everything should be reflected.
  • Provision a connection to your blockchain node. Either with Web3modal, which supports many wallets, or directly to your hardhat node through HttpRPC.

# Quik start

If you want to quickly get started with a new hardhat project and a react application. Try this boilerplate(opens new window) .

# Get started

# Install plugin

Yarn: yarn add --dev @symfoni/hardhat-react

NPM: npm install --save-dev @symfoni/hardhat-react

# Install peer dependencies

Yarn: yarn add --dev hardhat hardhat-deploy hardhat-deploy-ethers hardhat-typechain hardhat-typechain ts-morph ts-node typescript ts-generator typechain typechain-target-ethers-v5

NPM: npm install --save-dev hardhat hardhat-deploy hardhat-deploy-ethers hardhat-typechain hardhat-typechain ts-morph ts-node typescript ts-generator typechain typechain-target-ethers-v5

# import plugins

To import plugins into your hardhat project.

If javascript project, hardhat.config.js

require("@nomiclabs/hardhat-ethers");
require("hardhat-deploy-ethers");
require("hardhat-deploy");
require("@symfoni/hardhat-react");
require("hardhat-typechain");
require("typechain-target-ethers-v5");

if typescript project, hardhat.config.ts

import "@nomiclabs/hardhat-waffle";
import "@nomiclabs/hardhat-ethers";
import "hardhat-deploy-ethers";
import "hardhat-deploy";
import "@symfoni/hardhat-react";
import "hardhat-typechain";
import "typechain-target-ethers-v5";

# Runtime

The plugin will hooks into hardhat-deploy, which hooks into npx hardhat node --watch. Therefore, the plugin will run when you are starting up a node or making changes to a solidity file or deploy file.

You can run it manually with npx hardhat react. You probably need to run npx hardhat typechain and npx hardhat deployfirst to have artifacts, deployments, and typechain files ready.

The React context uses the output from typechain and deployments. It generates a react context component as a typescript react file (HardhatContext.tsx), which imports both typechain and deployment files. It then uses these files alongside Ethers and Web3Modal to set up a context for your connection and each smart contract (deployed or not deployed).

# Frontend

This plugin assumes that you are building your frontend inside a hardhat project (we later want to go away from this assumption). So we recommend you create a frontend folder inside your hardhat project where all your frontend code and packages reside. Take a look at https://github.com/symfoni/hardhat-react-boilerplate for a demonstration.

Most frontend projects require all file dependencies to be inside that folder. Therefore we suggest (and default ) your typechain and deployments path to "./frontend/src/hardhat/{deployments | typechain}". The Hardhat context (a react component .tsx) file will also default to this folder. Though you are free to set whatever paths you like, and it should resolve relative to your config.paths.react folder.

# Frontend dependencies

You will need to install these dependencies in your frontend.

Yarn: yarn add ethers web3modal

NPM: npm install --save ethers web3modal

# Frontend "framework"

You are free to choose whatever React typescript "framework". We have only tested with Create React App for now.

Create React app can be initialized in your Hardhat root folder with:

NPX: npx create-react-app frontend --template typescript

# React component

To use the React component in a React application. Import the HardhatContext.tsx file in your app.

import { HardhatContext } from "./hardhat/HardhatContext";

Then wrap everything or the components in this context as a provider.

<HardhatContext>
  <SomeComponent></SomeComponent>
  <SomeOtherComponent></SomeComponent>
</HardhatContext>

To use a contract, import that context into the component that needs it.

import { GreeterContext } from "./../hardhat/HardhatContext";

Then use it as a state in that component.

const greeter = useContext(GreeterContext);

# Contract context

The contract context gives you two properties.

export interface ContractContext {
  instance?: Contract;
  factory?: ContractFactory;
}

If the Hardhatcontext successfully connected to a provider in your frontend (web3modal or Hardhat node) and

  • hardhat-deploy deployed an instance of your contract. The ContractContext.instance property will be initiated. Here you have access to all functions, events, and so on.
  • the provider has Sing functionality. The ContractContext.factory will be available, and you can deploy a contract or connect to other instances.

# Component examples

# Hardhat context example

import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { HardhatContext } from "./hardhat/HardhatContext";
import { Greeter } from "./components/Greeter";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <HardhatContext>
          <Greeter></Greeter>
        </HardhatContext>
      </header>
    </div>
  );
}

export default App;

# Contract context example

The contract is named Greeeter.sol

import React, { useContext, useEffect, useState } from "react";
import { GreeterContext } from "./../hardhat/HardhatContext";

interface Props {}

export const Greeter: React.FC<Props> = () => {
  const greeter = useContext(GreeterContext);
  const [message, setMessage] = useState("");
  const [inputGreeting, setInputGreeting] = useState("");
  useEffect(() => {
    const doAsync = async () => {
      if (!greeter.instance) return;
      console.log("Greeter is deployed at ", greeter.instance.address);
      setMessage(await greeter.instance.greet());
    };
    doAsync();
  }, [greeter]);

  const handleSetGreeting = async (
    e: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => {
    e.preventDefault();
    if (!greeter.instance) throw Error("Greeter instance not ready");
    if (greeter.instance) {
      const tx = await greeter.instance.setGreeting(inputGreeting);
      console.log("setGreeting tx", tx);
      await tx.wait();
      console.log(
        "New greeting mined, result: ",
        await greeter.instance.greet()
      );
    }
  };
  return (
    <div>
      <p>{message}</p>
      <input onChange={(e) => setInputGreeting(e.target.value)}></input>
      <button onClick={(e) => handleSetGreeting(e)}>Set greeting</button>
    </div>
  );
};

# ProviderContext

It gives you a context to your current provider and the ability to change it.

import { ProviderContext } from "./../hardhat/HardhatContext";
...
const [provider, setProvider] = useContext(ProviderContext)

# SignerContext

It gives you a context to your current signer and the ability to change it.

import { SignerContext } from "./../hardhat/HardhatContext";
...
const [signer, setSigner] = useContext(SignerContext)

# CurrentAddressContext

It gives you a context to your current address and the ability to change it.

import { CurrentAddressContext } from "./../hardhat/HardhatContext";
...
const [currentAddress, setCurrentAddress] = useContext(CurrentAddressContext)

# Configuration

Our goal with this plugin was to make it easier for new developers to try out smart-contract development. Therefore we default the most needed configuration.

# Provider priority

The React context tries to connect the frontend up with an Ethereum provider. Here you can set that priority. In this scenario, the react context will try to connect with Web3modal(Metamask) first, then if that fails. Try to connect with your Hardhat node.

{
  "react": {
    "providerPriority": ["web3modal", "hardhat"]
  }
}

Later, we will add the possibility to set all config networks providers, URLs, etc. as provider priority. We stole this concept from Embark(opens new window) . Props to them.

# Paths React

HardhatContext.tsx (the React component) will be written to this path.

{
  "paths": {
    "react": "./frontend/src/hardhat"
  }
}

# Defaults

If you don't set these configurations yourself, the hardhat-react plugin will default to this.

{
  "react": {
    "providerPriority": ["web3modal", "hardhat"]
  },
  "paths": {
    "react": "./frontend/src/hardhat",
    "deployments": "./frontend/src/hardhat/deployments/"
  },
  "namedAccounts": {
    "deployer": {
      "default": 0
    }
  },
  "typechain": {
    "outDir": "./frontend/src/hardhat/typechain",
    "target": "ethers-v5"
  }
}

# Projects

# Caveats

# Users get Metamask up in their faces right after they enter the webpage. BAD UX!

Yea, we know. We have some patterns with a fallback provider that solves this. But for the hackathon, we have not that had time to implement that. Coming

# Can I only use Metemask?

Web3modal supports many wallets. We will soon provide configuration for each of them.

We will also allow you to inject your "whatever" hardhat development node you are using as a fallback provider when developing. We think this is a friendly tool for new Ethereum developers also as then they don't need to wrap their heads around a provider while building a smart-contract and a provider in the frontend.

# Invalid nonce.

eth_sendRawTransaction
  Invalid nonce. Expected X but got X.

Reset your account in Metamask.

# Why cant the react component be built as a package which I can import.

We don't know enough of the react build process to efficiently create a typescript react component which can be consumed by any other React build process. This is something we want to achieve!

# Do I have to create a context of all contracts?

It will do so by default, but we will later provide an option to be explicit about which contracts create a context.

# Will you support other frontend frameworks?

Not planned atm.

# Development

If you want to develop this plugin in any way, we suggest you fork this Lerna repo; hardhat-plugins(opens new window) .