Full Stack Ethereum and Dapp Development. A comprehensive guide: 2024
Tags: blockchain, ethereum, hardhat, celo
Published: 2024-05-27 11:31:43

## Building Full stack Dapps with Solidity, Hardhat, React, EthersJs, Mocha and Chai.

Building decentralized applications on the [Ethereum](https://ethereum.org/en/) blockchain and other EVM-compatible blockchain networks such as [Polygon](https://polygon.technology/), [Avalanche](https://www.avax.network/), [Celo](https://www.celo.org/) and many others comes handy in this tutorial.
At the conclusion of this comprehensive guide, you will understand every step required for the concise development and effective deployment of smart contracts, as well as the proper integration of the client-side with React.

After considering numerous options for the tutorial's heading, I decided to include the year of publishing to raise awareness about the tutorial's goals and objectives. Blockchain is a nascent and rapidly expanding technology with several modifications being made to the development method on a daily basis. I'm sure some developers have seen various tutorials that depict comparable teaching, but the methodologies may have altered over time as a result of the changes brought to blockchain development every now and then.

Based on my knowledge and experience as a blockchain developer, the most popular stack being utilized by Web3 developers for building a full stack decentralized application with Solidity includes:


1. Ethereum Development Environment - Hardhat
2. Ethereum Web Client Library - Ethers.js
3. Javascript Test Framework - Mocha
4. Client-side framework - React
5. Oracle network - Chainlink 
6. API Layer - The Graph Protocol

To further understand the concept behind the stack mentioned above, I recommend you check out this tutorial.
{% link https://dev.to/azeezabidoye/roadmap-to-blockchain-development-2024-4c9l %}

As I previously stated, the Web3 industry is always growing, with new updates being provided on a daily basis. As a result, I have compiled the most recent complete strategy to simplify blockchain development and will hopefully update this guide as needed.


> The code for this tutorial is located here:
{% github azeezabidoye/messenger-dapp %}


## What we intend to achieve
In this tutorial, we are going to learn how to:

- Create Dapp using Hardhat framework
- Write Solidity smart contract
- Deploy to Celo Alfajores testnet
- Test our Dapp using Mocha and Chai
- Create UI components with React
- Interact with our Dapp


## The Stack breakdown

1. **Hardhat**: To build smart contracts, you must compile your Solidity code into code that can be easily read and run by the client-side application, deploy your contracts, perform tests, and debug Solidity code without dealing with a live environment.
[Hardhat](https://hardhat.org/docs) is an Ethereum development environment and framework created specifically for this purpose. We will build our full stack Dapp using the Hardhat framework.


2. **React**: [React](https://react.dev/) is an excellent frontend Javascript library for developing web applications, user interfaces, and UI components.
React and its extensive ecosystem of metaframeworks, including [Next.Js](https://nextjs.org/), [Gatsby](https://www.gatsbyjs.com/), [Blitz.Js](https://blitzjs.com/) and others support a wide range of deployments, including classic Single Page Applications (SPAs), static site generators, server-side rendering, and a combination of the three.
We will construct our Dapp by combining React with Hardhat as the client-side library. 


3. **Ethers.js**: The [Ethers.js](https://docs.ethers.org/v5/getting-started/) library will serve the purpose of the Ethereum web client library in the development of our Dapp.
In our React application, we'll need to interact with the deployed smart contracts. We'll need a means to read the data and send new transactions.
Ethers.js intends to provide a comprehensive library for the Ethereum blockchain and its ecosystem, covering client-side Javascript applications such as [React](https://react.dev/), [Vue](https://vuejs.org/), and [Angular](https://angularjs.org/).


4. **Mocha**: According to the [Mocha](https://mochajs.org/) website, it is a Javascript framework that makes asynchronous testing simple and fun.
Before we deploy our Dapp to the blockchain network, we need to execute a series of tests to ensure that the smart contracts function properly.
Mocha tests run serially, allowing for flexible and reliable reporting; this is the library we will use in our projects.


5. **Metamask** is a Google Chrome extension that injects itself into Javascript code anytime your Dapp frontend is loaded.
This Chrome extension assists with account administration and connects the current user to the blockchain.
Once a user has connected their [Metamask](https://metamask.io) wallet, you as a developer can interact with the globally available Ethereum API (`windows.ethereum`), which identifies users of web3-compatible browsers (such as Metamask), and whenever you request a transaction signature, Metamask will prompt the user to confirm it immediately. 


6. **Chainlink**: [Chainlink](https://chain.link/) bridges a major gap in the blockchain ecosystem by enabling smart contracts to safely communicate with real-world apps, increasing their use cases and effectiveness.
Chainlink is a significant decentralized oracle network that enables data interchange between on-chain and off-chain applications. It is essential for providing real-world information to blockchain networks.


7. **The Graph**: Because most blockchain applications, such as Ethereum, are difficult and time-consuming to read data from the chain, both companies and individuals create their own centralized indexing servers and service API requests from them. Unfortunately, this requires a significant investment in engineering and hardware, as well as a compromise of the security features essential for decentralization.
[The Graph Protocol](https://thegraph.com/) is an indexing protocol for searching blockchain data that allows for the development of completely decentralized apps while also offering a rich GraphQL query layer for application consumption.


The tutorial is absolutely beginner-friendly thereby for some reasons we will not discuss Chainlink and The Graph. However, there will be reasons to reference these tools in tutorials in the future.

## Prerequisites

- NodeJs
- Metamask
- Testnet ethers


## Dev tool

- Yarn

```shell
npm install -g yarn
```

## Let's start hacking...

### Step #1: Create new React project

To get started, create a new React application

```shell
npm create vite@latest project_name --template react
```
In our case, we will name the application `messenger-dapp`. Therefore, you can run:
```shell
npm create vite@latest messenger-dapp --template react
```

Follow the prompt, choose React and finally choose Javascript.

> ✍️ Your React app is created with the project-name specified.

Navigate to the new project directory.

```shell
cd messenger-dapp
```

### Step #2: Install Hardhat package as a dependency
You can use either **Yarn** or **NPM** but for the purpose of this tutorial, I will recommend using **Yarn**
```shell
yarn add hardhat
```

### Step #3: Configure Hardhat Ethereum Development environment
```shell
npx hardhat init
```
Follow the prompt and select `Create a Javascript project`.
Press “`y`" to agree to other options and continue.

> ✍️ Hardhat will automatically install all necessary packages for your project.


#### Hardhat folder structure
- **Hardhat.config.cjs file**: Serves as the entry point of our development; includes every configuration, plugins, and custom tasks.
- **Contracts directory**: Directory for all Solidity contract codes.
- **Test**: Directory for test scripts.


![Hardhat folder structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1em6boxnv375c8e0hliq.png)

Delete all the files in both Contracts and Test directories. This is to ensure that we have a clean slate for our code and development.

### Step #4: Setup environment variables
Environment variables are predetermined values that are typically used to provide the ability to configure the way programs, applications and services will behave.
For this tutorial, there are two basic environment variables we will be needing for our development. They are; an **Infura API key** which will help us to run our node during deployment and our **Private key**.

#### Getting started with Infura API key
Let me quickly walk you through the process of getting your first API key on the Infura platform

- Visit [infura.io](https://infura.io)
- Get started by setting up an Infura account
- Select "Create new API key" on the dashboard.
Follow the first two steps in this [documentation](https://www.infura.io/blog/post/getting-started-with-infura-28e41844cc89) for guidance
- Click on the API key name to see all endpoints provided
- Check your desired network endpoints and "Save Changes"

![Infura All Endpoints page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nuq620zap8d2s6c4y2po.png)
- Select "Active Endpoints" in the navigation.
- Copy the HTTPS URL provided for your testnet.

![Infura Active Endpoint page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54xquzw1wmjnc6nerr3t.png)

#### BONUS: How to Integrate Celo Alfajores Testnet with Metamask
- Open [Metamask](https://metamask.io)
- Select the network dropdown at the top left corner
- Turn on the “Show testnet” option to see test networks

![Metamask network dropdown](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xogwrrn2eb8zx0ig3u5b.png)
- To add Celo Alfajores testnet to the list; visit [chainlist.org](https://chainlist.org)
- Endeavour to check the “Include Testnets” checkbox
- Search for “Celo Alfajores” in the search field
- Click “Add to Metamask” on the network grid

![Chainlist network page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k02xo1mvxapde7ntjqjj.png)
- Follow the prompt and finally “Switch network”
- Get free tokens from Celo faucet; visit [faucet.celo.org](https://faucet.celo.org)
- Paste wallet address into the “Account address” field
- Click on “Faucet” button

![Celo faucet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xrm23eh7s3zh9rgznvb.png)
- Wait for process to be completed
> 🎉 Boom: Your account has been funded with 0.5 Celo token

![Celo Token balance on Metamask](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z45qcmb4nqq07vq03h98.png)

#### Configure Private key
- Open [Metamask](https://metamask.io)
- Click the stacked dots at the top right corner
- Select "Account details"

![Metamask account details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ivuh9o2ec587nr8vdla.png)
- Select "Show private key"

![Show Private key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e71ydgb0w5g3b6tevf3m.png)
- Enter your password to continue

![Insert password on Metamask](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cezoozv7pe2hyu6nf5zb.png)
- Click and hold "Hold to reveal Private Key" button to reveal your Private key

![Hold to reveal private key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vbog8zawwhadgssb8q4h.png)
- Copy your Private key for configuration

![Copy private key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t59lx9s4b2c62verobkb.png)

### Step #5: Configure Hardhat for Dapp development
Navigate to the `hardhat.config.cjs` file and configure the network for the testnet.

```javascript
  networks: {
    alfajores: {
      chainId: 44787,
      url: "https://celo-alfajores.infura.io/v3/1644a8878efe4a5e8b1eabc92564e725", // Insert Infura Celo Url here
      accounts: [a77868cba9d67ed2854547cdebb3e30c52cabaa1c4646beefing786c811c5fc], // Insert Metamask Private key here
    }
  }
```
> Endeavour to prefix your Private key with an `0x` and wrap it in quotes to avoid errors. 

```javascript
  networks: {
    alfajores: {
      // Code here
      accounts: ["0xa77868cba9d67ed2854547cdebb3e30c52cabaa1c4646beef008e786c811c5fc"], // Insert Metamask Private key here
    }
  }
```

### Step #6: Create the smart contract file
Navigate to the `Contracts` directory and create a new file for the Solidity code as `Messenger.sol` and update the file with the following code:


```javascript
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "hardhat/console.sol";

contract Messenger {
    string message;

    constructor(string memory _message) {
        console.log("Deploying Messenger with message:", _message);
        message = _message;
    }

    function getMessage() public view returns (string memory) {
        return message;
    }

    function setMessage(string memory _message) public {
        console.log("Changing message from '%s' to '%s'", message, _message);
        message = _message;
    }
}
```

#### About the contract
This smart contract is simple. The contract has a variable that was defined in the global scope but assigned a value in the function constructor.
The function constructor is only called when the contract is deployed, therefore it sets the `message` variable. It also exposes a function (`getMessage`) that can be used to retrieve the message.
Additionally, another function called (`setMessage`) is available, which allows users to modify the message variable. When this contract gets deployed on the Ethereum blockchain, users will be able to interact with these methods.

#### Reading and writing to the Ethereum blockchain

There are two main ways to interact with an Ethereum smart contract: reading and writing. Reading is a non-transactional activity, whereas writing is transactional. In the smart contract shown above, the (`getMessage`) function is considered reading, whereas the (`setMessage`) method is considered writing or transactional.

You do not need to carry out a transaction if you are merely reading from the blockchain and not modifying or updating anything; there will be no gas or cost involved. The function you request is then carried out exclusively by the node to which you are connected, thus you do not have to pay for gas and reading is free.

However, while writing or initializing a transaction, you must pay for it to be included in the blockchain. To make this work, you must pay gas, which is the cost or price necessary to properly complete a transaction or execute a contract on the Ethereum blockchain.

From our client-side application, we will communicate with the smart contract using the ethers.js library, the contract address, and the ABI generated by hardhat from the contract.



### Step #7: Compile the contract
Compiling a smart contract involves using the contract's source code to generate its bytecode and the contract Application Binary Interface (ABI). The Ethereum Virtual Machine (EVM) executes the bytecode to understand and execute the smart contract.

After we run the command to compile the smart contract, Hardhat generates a directory named `artifacts` in our root directory. 
We can specify where the `artifacts` directory should be simply by adding a few options to the `hardhat.config.cjs` file.

```javascript
  paths: {
    artifacts: "./src/artifacts",
  }
```

> ⚠️ Specifying a directory for the auto-generated ABI has no bearing on the smart contract compilation process. It's just a good practice to place the ABI in the `src` folder because that's where our client-side code will be created.

As of now, your Hardhat configuration should look like this:

```javascript
module.exports = {
  solidity: "0.8.24",
  paths: {
    artifacts: "./src/artifacts"
  },
  networks: {
    alfajores: {
      chainId: 44787,
      url: "https://celo-alfajores.infura.io/v3/1644a8878efe4a5e8b1eabc92564e725", // Insert Infura Celo Url here
      accounts: ["0xa77868cba9d67ed2854547cdebb3e30c52cabaa1c4646beefinge786c811c5fc"], // Insert Metamask Private key here
    }
  }
};
```


#### What is an ABI?

ABI stands for application binary interface. You might consider it as an interface between your client-side application and the Ethereum blockchain, where the smart contract with which you will be interacting with is created.

Now that we've covered the fundamentals of smart contracts and are familiar with ABIs, let's create one for our project.


```shell
yarn hardhat compile
```

> ✍️ Watch out for the `artifacts` which is automatically added to the `src` directory of your project.


### Step #8: Configure the Dapp for deployment
This is the most important stage of Dapp development; a few settings must be completed for a timely and effective deployment.

Create a folder for deployment scripts in the root directory

```shell
mkdir deploy
```

Create a file for the deployment scripts in the `deploy` directory with a numbered naming structure. e.g `00-deploy-messenger.cjs`


Install an Hardhat plugin as a package for deployment 
```shell
yarn add hardhat-deploy --dev
```

Import `hardhat-deploy` package to the `hardhat-config.cjs` file

```javascript
require("hardhat-deploy")
```

Install `hardhat-deploy-ethers` to override the `@nomiclabs/hardhat-ethers` package
```shell
yarn add --dev @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers
```

> ✍️ The command above allows Ethers to keep track of and remember all of the multiple deployments that we perform within our contract.

Set up a deployer account in the `hardhat-config.cjs` file

```javascript
networks: {
  // Code Here
},
namedAccounts: {
  deployer: {
    default: 0,
  }
}
```

Update the `00-deploy-messenger.cjs` file with the following code to deploy the `Messenger` contract

```javascript
module.exports = async ({ getNamedAccounts, deployments }) => {
  const { deploy } = deployments;
  const { deployer } = await getNamedAccounts();
  await deploy("Messenger", {
    contract: "Messenger",
    from: deployer,
    args: ["Hello Devs...this is simple and fun"], // The message value in the function constructor
    log: true, // Logs statements to console
  });
};
module.exports.tags = ["Messenger"];
```

### Step #9: Deploy contract to testnet
Now, we can execute the deploy script and instruct the CLI that we want to deploy to Celo Alfajores test network.

```shell
yarn hardhat deploy --network alfajores
```
Once this script is executed, the smart contract should be deployed to the Celo Alfajores test network, enabling us to interact with it.

The output of the CLI should look like this:

```shell
deploying "Messenger" (tx: 0x59ef946ed3b481f78ea04929e4a1724aeccf7a3598fe7900e269e05ed90d4385)...: deployed at 0xAf22bD61d2206D22050C524003017817DebE61e4 with 633051 gas
✨  Done in 30.37s.
```

And here is the contract address: 

```shell
deployed at 0xAf22bD61d2206D22050C524003017817DebE61e4
```

> ✍️ Observe how the token balance has changed. Gas fee for the Dapp's deployment have been deducted from the token balance. We would encounter more of this anytime we made transactional requests to the blockchain.


![Token balance on Metamask after deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yeyau7u4ia5keozxst69.png)

### Step #10: Create and connect UI components with React

In this tutorial, we will create a basic UI component using React. To get you started, we'll focus on essential functions and a few CSS styles.

Let us briefly explore the two major goals of our React application:

1. Retrieve the current value of `message` from the smart contract.
2. Allow a user to update the value of the `message`. 

Here are the few steps we need to take to achieve these goals: 

1. Create an input field and some local state to handle the input value (to update the message).
2. Allow the application to connect to the user's MetaMask account for signing transactions.
3. Create functions that read and write to the smart contract.

Open the `src/App.jsx` file and update it with the following code, set the value of `messengerContractAddress` to the address of your smart contract:

```javascript
import "./App.css";
import React from "react";
import { useState } from "react";
import { ethers, BrowserProvider } from "ethers";

// Import the json-file from the ABI
import Messenger from "./artifacts/contracts/Messenger.sol/Messenger.json";

// Store the contract address in a variable
const messengerContractAddress = "your-contract-address"; // Deployed to testnet

const App = () => {
	// Store message in a local state
	const [message, setMessageValue] = useState();

	// Request access to User's MetaMask account
	const requestAccount = async () => {
		await window.ethereum.request({ method: "eth_requestAccounts" });
	};

  	// Function for retrieving message value from smart contract.
	const getMessage = async () => {
		if (typeof window.ethereum !== "undefined") {
		const web3Provider = new ethers.BrowserProvider(window.ethereum);
		const contract = new ethers.Contract(
			messengerContractAddress,
			Messenger.abi,
			web3Provider
		);
		try {
			const data = await contract.getMessage();
			console.log(`Data: ${data}`);
		} catch (error) {
			console.error(error);
		}
		}
	};

	// Function for updating message value on smart contract
	const setMessage = async () => {
		if (!message) return;
		if (typeof window.ethereum !== "undefined") {
		await requestAccount();
		const web3Provider = new ethers.BrowserProvider(window.ethereum);
		const signer = await web3Provider.getSigner();
		const contract = new ethers.Contract(
			messengerContractAddress,
			Messenger.abi,
			signer
		);
		const transaction = await contract.setMessage(message);
		await transaction.wait();
		setMessageValue("");
		getMessage();
		}
  };

  return (
    <div className="container">
      <button onClick={getMessage}>Message</button>
      <button onClick={setMessage}>Send message</button>

      <input
        onChange={(e) => setMessageValue(e.target.value)}
        placeholder="Write your message here..."
      />
    </div>
  );
};

export default App;
```

#### Let's add some styles...
Update the `App.css` file with the following CSS code: 

```css
#root {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem;
  text-align: center;
}

/* styles.css */
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

button {
  background-color: #4caf50; /* Green */
  border: none;
  color: white;
  padding: 10px 20px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  margin: 10px;
  cursor: pointer;
  border-radius: 5px;
  transition: background-color 0.3s ease;
}

button:hover {
  background-color: #45a049;
}

input {
  padding: 10px;
  margin: 10px;
  width: 300px;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 16px;
  transition: border-color 0.3s ease;
}

input:focus {
  border-color: #4caf50;
  outline: none;
}
```

Next: run the React app:

```shell
yarn run dev
```

![Dapp interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/htt8eyh4j07m1jii3lf4.png)

Now, you can interact with your Dapp. Ensure to keep your `console` open to see results.


![Metamask transaction request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwz8ipuc3ge7re7r04ps.png)



![Data result in the console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2oulveav68quaez5x0ed.png)


### Step #11: Writing unit tests with Mocha and Chai

Unit testing applies to everything we want to test, whether it's a class, a function, or a single line of code.
In this article, we will look at unit testing our Solidity code using Mocha, a lightweight Nodejs framework, and Chai, a Test-driven development (TDD) assertion library for Node.
Both Mocha and Chai use NodeJs, and the browser supports asynchronous testing. Although Mocha may be used with any assertion library, it is most usually combined with Chai.

Let's initiate some tests for the deployment of the smart contract and the two functions it contains.

First, ensure you have the necessary dependencies installed:

```shell
yarn add mocha chai@4.3.7 --dev
```

Navigate to the `test` directory and create a new file as `messenger-test.cjs`.

Add the following code to `test/messenger-test.cjs`:

```javascript
const { ethers } = require("hardhat");
const { expect } = require("chai");

describe("Messenger", function () {
  let MessengerFactory, messenger;

  beforeEach(async function () {
    MessengerFactory = await ethers.getContractFactory("Messenger");
    messenger = await MessengerFactory.deploy(
      "Hello devs...we love EVM development"
    );
    await messenger.waitForDeployment();
  });

  describe("Deployment", function () {
    it("Should set the correct initial message", async function () {
      expect(await messenger.getMessage()).to.equal(
        "Hello devs...we love EVM development"
      );
    });
  });

  describe("SetMessage", function () {
    it("Should change the message when called", async function () {
      await messenger.setMessage("We love building Dapps");
      expect(await messenger.getMessage()).to.equal("We love building Dapps");
    });

    it("Should emit console log on message change", async function () {
      const transaction = await messenger.setMessage("Happy hacking...");
      await transaction.wait();
    });
  });
});
```

Navigate to your terminal and run:

```
yarn hardhat test
```

The result of your test should pass like this:

```shell
  Messenger
    Deployment
Deploying Messenger with message: Hello devs...we love EVM development
      ✔ Should set the correct initial message
    SetMessage
Deploying Messenger with message: Hello devs...we love EVM development
Changing message from 'Hello devs...we love EVM development' to 'We love building Dapps'
      ✔ Should change the message when called
Deploying Messenger with message: Hello devs...we love EVM development
Changing message from 'Hello devs...we love EVM development' to 'Happy hacking...'
      ✔ Should emit console log on message change


  3 passing (412ms)

✨  Done in 1.78s.
```

#### Big ups to you 👍
Congratulations if you've made it this far, and I commend your determination and desire to learn Web3 development. This is only a basic approach to the development, and I hope you use these techniques whenever you create a decentralized application for Ethereum and other EVM-based blockchain networks. 
Although we have addressed certain elements required for effective development, there is something more we must do as professionals.

### BONUS: Secure your environment variables
Remember, in **Step #4** of this tutorial, we set up our environment variables, which are third-party components required for the creation and deployment of our smart contract.
It is critical that we safeguard them; if the codebase is accidentally shared or made public, the embedded secrets are revealed, resulting in a possible security compromise. Environment variables, on the other hand, are kept on the server and not exposed in the code, minimizing the danger of exposure.

We must protect the `API endpoint` we got from Infura and our Metamask `Private key`.

Install the dependency module that loads environment variables from a `.env` file:

```shell
yarn add dotenv --dev
```

Create a new file in the root directory of the project as `.env`.

Add two new variables to the `.env` file with their values as follows:

```javascript
PRIVATE_KEY="a77868cba9d67ed2854547cdebb3e30c52cabaa1c4646beefinge786c811c5fc"
INFURA_ALFAJORES_URL="https://celo-alfajores.infura.io/v3/1644a8878efe4a5e8b1eabc92564e725"
```

Import the `dotenv` module to `hardhat-config.cjs` for configuration

```javascript
require("dotenv").config()
```

Replicate the two environment variables in `hardhat-config.cjs` file

```javascript
const { PRIVATE_KEY, INFURA_ALFAJORES_URL } = process.env;
```

Finally, your `hardhat-config.cjs` should be detailed as follows:

```javascript
require("@nomicfoundation/hardhat-toolbox");
require("hardhat-deploy");
require("dotenv").config();

const { PRIVATE_KEY, INFURA_ALFAJORES_URL } = process.env;

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.24",
  paths: {
    artifacts: "./src/artifacts",
  },
  networks: {
    alfajores: {
      chainId: 44787,
      url: INFURA_ALFAJORES_URL,
      accounts: [PRIVATE_KEY],
    },
  },
  namedAccounts: {
    deployer: {
      default: 0,
    },
  },
};
```

## Conclusion 
Finally, we covered the fundamentals of Ethereum and Dapp development in great depth. Let's take a brief look at what we've learnt thus far;

1. ✅ Built Dapp using Hardhat framework 
2. ✅ Developed Solidity smart contract.
3. ✅ Deployed to Celo Alfajores testnet.
4. ✅ We tested our DApp.
5. ✅ Built a client-side application with React.
6. ✅ Interacted with the Dapp.
7. ✅ Additionally, secure the environment variables.

In my future tutorials, I'll go over more complex smart contract development, as well as how to use Chainlink to bridge communication between on-chain and off-chain networks, as well as how to deploy smart contracts as subgraphs to expose a GraphQL API and implement things like pagination and full text search.

If you find this lesson useful, please upvote and share it so that others can benefit as well. If you encounter any issues or have recommendations for future tutorials, please leave a comment and let me know.