Introduction:
The rapid evolution of blockchain technology has paved the way for Web 3.0 and decentralized finance, a new era that is redefining the way we transact, invest, and interact with our money.
This article delves into building SendCrypto, a Web 3.0 application that enables users to perform transactions through the blockchain.
What you will be building:
You will be creating a fully-fledged Web 3.0 application that allows users to send transactions through the blockchain.
Technologies you will be using for this project include React for the front-end, Hardhat-Ethers for Ethereum integration, and Solidity for the smart contract.
Application Demo:
View the application Demo Here
If you do not have a Meta mask setup on your browser, You can do that using this link - https://support.metamask.io/hc/en-us/articles/360015489531-Getting-started-with-MetaMask
Here is the link to the github repository
How it works:
The application allows users to make a transaction through the blockchain.
-Each transaction is paired with an image and it is stored on the blockchain.
When a user clicks on the connect to wallet button, it will immediately trigger a connection to the Metamask wallet, and it will request the account you want to connect to.
Connection to a meta mask account - You will automatically see a display of the connected wallet address on the Ethereum card.
You can switch your account to another account you want to send Ethereum to from the Metamask wallet.
Input your wallet address on the transaction area. Specify the amount of Ethereum you want to send, add a keyword you would want to use and a message then click on the Send Now button
Metamask automatically prompts a message describing the amount to be sent.
-Click on confirm then a second notification will appear which is the contract interaction.
Here, we are actually interacting with our Solidity smart contract doing this.
If you scroll down, you will see the recent transactions being stored on the blockchain. Clicking on any transaction will take you to the Etherscan platform, where you will see the actual address and the real transaction.
The focus of this tutorial will be to implement Web 3.0 into our React application, allowing it to send transactions through the blockchain and then store those transactions.
We will be conducting our testing on the goerli faucet test network.
Lets setup our development environment.
Setting up the Development Environment:
Now let's configure our development environment. We will start by setting up the frontend of the application using React.
Before we begin, ensure you have Node.js and npm (Node Package Manager) installed on your machine. You can download them from the official Node.js website: https://nodejs.org/
To verify if Node.js and npm are installed, open your terminal and run the following commands:
node -v
npm -v
Create a New Project:
create the main project folder, name it Sendcrypto.
We will be using vite to bootstrap our application. Vite is a build tool and development server for modern web development. It helps you set up a modern development environment with minimal configuration.
To install Vite globally, run the following command:
npm create vite@latest
Then follow the instructions to install it.
navigate into your project folder and run this command:
npm install
npm run dev
Ensure you you name the react application as client during installation.
Your application should be running on http://127.0.0.1:5173/ . Enter this address on your browser. If the installation is successful, you should see this on your browser:
Installing tailwind CSS
use these commands
`npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p`
Open your project with VScode editor
copy the code below and paste on your tailwind.config file that was created when you ran the above command.
module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", ], theme: { extend: {}, }, plugins: [],}
Add these code (directives) below to your index.css file
@tailwind base;
@tailwind components;
@tailwind utilities;
You can now test your App by adding the code below in your app.js
file
to ensure your tailwind style is working.
Now let's create the basic setup for our smart contract. Navigate into the smart_contract directory. and run the command:
npm init -y
This will initialize an empty package.json
. This would be the starting point of our smart contract.
Now let's create our react application that will display our transactions on the frontend.
Navigate to your client
folder of your react application. Add a folder in the src
directory. Name it components.
We can start adding our components. Inside the components folder, add the following files Navbar.jsx
Footer.jsx
, Services.jsx
, Loader.jsx
Transactions.jsx
Welcome.jsx
To allow us to export all these components from a single file, add a file in the components folder. Name it index.js
Export all the components you have created in the index.js file. It should now look like this.
Add the following basic contents to the components you have created.
Now update your App.jsx
by importing these components into them.
Your App.jsx
should now look like this.
If everything works fine you should now see this on your browser.
Adding style to our App:
To add style to our application simply copy the style below and add it to your index.css file.
For the icons we will use throughout the project, install the react icons using this command:
npm i react-icons
For the logo, create a folder inside the src
folder and add add a logo image of your choice to it.
Now let's add the code for the Navbar component:
Open the Navbar.jsx
file and add the following code:
Here in this code, we imported the required modules and resources. We imported React from the "react" package, icons like HiMenuAlt4 from the react-icons/hi
package, and AiOutlineClose
from the react-icons/ai
package.
We then added a logo image by importing it from the "../images/logo.png" file
We also define a functional component NavBarItem
. It takes two props, title
and classprops
.
Inside the component, it returns a list element with the title as its content and applies CSS classes for margin, cursor, and any additional classes provided via classprops
.
Inside our Navbar component,we sets up a state variable toggleMenu
and a state updater function setToggleMenu
using the useState
hook. The initial state of toggleMenu
is false.
In our jsx
we, render a responsive navigation bar component that includes a logo, navigation items, and a mobile menu toggle.
It adapts to both desktop and mobile screen sizes, providing easy navigation through a list of items and a mobile menu toggle.
Your Navbar
should now look like this on the browser:
Now let's add the code for the Welcome
component.
Open the Welcome.jsx
file and add the code below:
Here in this Welcome
component, we updated it by first we import of several icons from the "react-icons" library.
We define a constant companyCommonStyles
.This contains a set of CSS classes.
We also define a functional component called Input
. It takes props like placeholder, name, type, value, and handleChange
, and renders an input element with various properties and styling.
Inside the Welcome
component, we declared three functions - connectWallet
and handleSubmit
and an empty handleChange
function. We will add the logic for these functions later
The return statement of the Welcome component starts by rendering a structure of HTML elements.
The content section includes headings, a description, and a "Connect Wallet" button.
We added a section that includes a grid of information boxes with text like "Reliability", "Security", "Ethereum", etc. These boxes represent features of the platform.
We also added a button to send crypto.
Your application should now look like this.
Adding the Service Component:
Now inside your client folder, open the Services.jsx file and the following code :
Here in this code, we imported several icons from the react-icons
package.
We then define a functional component ServiceCard
that takes several props: color, title, icon, and subtitle.
This component represents a card-like structure containing service details.
We use the color prop to determine the background color of the circular icon container.
The icon prop is a React element representing the icon for the service.
The title prop is the title of the service.
The subtitle prop is the description of the service.
The card structure is built using flexbox classes for layout and styling.
We also define another functional component Services
.
This component wraps the content of the services section, providing a gradient background.
Inside the component, we added a nested div element with various styling classes applied to control layout and spacing.
Adding Footer Component:
Inside the Footer.jsx
file we previously created, add the following code:
Here in this code above, we define a React functional component called Footer, which serves as a footer section for our application.
The footer contains various elements such as a logo, navigation links, contact information, and copyright text.
We also imported the image named logo.png
from the ../images/ directory to be used for our logo.
Adding Helper functions:
We will be adding some helper functions and reusable code snippets that we will be using throughout the application.
Inside the src
folder, create a folder and name it utils
. create a file inside the utils
folder, name it Transactions.json
, and add the following code to it:
Here in this code, is a JSON object that describes an Ethereum
smart contract called "Transactions" written in Solidity.
It includes information about the contract's name, source file path, ABI (interface for external interaction), compiled bytecode, and deployed bytecode.
The ABI outlines the contract's functions and events, while the bytecode is the low-level code that runs on the Ethereum network.
This artifact is essential for deploying, interacting with, and understanding our contract programmatically.
Inside the utils
folder, create another file. Name it constants.js
and add the following code to it.
Here in this code above, we imported the ABI(interface for external interaction)
of the smart contract from the Transactions.json
file we created.
We declared a constant variable named contractAddress
, which contains the Ethereum
address where the smart contract has been deployed. This address is represented as a hexadecimal string.
export const contractABI = abi.abi;
In this line of code, we declared a constant variable named contractABI
. It assigns the abi property of the imported abi object to contractABI.
This contractABI
represents the ABI of the smart contract and will be used in various parts of the application to interact with the contract's functions and events.
What we have done in this whole code module is to provide a central location for storing the contract's address and ABI, so as to make it easy for us to access and use these details throughout the application.
Inside the utils file, add create another file. Name it shortenAddress.js
and add the following code:
Here in this code above, we define a function called shortenAddress
. It takes in an Ethereum address as input and returns a shortened version of that address.
Ethereum addresses are typically long hexadecimal strings, and this function is intended to make them more visually manageable.
So, when you call the shortenAddress
function with a full Ethereum address as an argument, it will return a string that shows the first five characters, an ellipsis, and the last four characters of the address.
Let's also add dummy transaction data that would serve as default transactions on the blockchain.
Inside the utils folder create a file. Name it dummyData.js
and add the following code to it:
Here in this code above, we exported an array of objects, each representing a transaction with certain details.
Each object in the array contains information about the sender's address, receiver's address, amount, timestamp, and a GIF URL.
This represent a series of transactions in our blockchain application.
Now next we will use the React Context API to manage and share the state and functions relating to Ethereum transactions across our components without the need to pass props explicitly through component nesting.
Inside the src
folder, create a folder. name it context. Inside the context folder, create a file. Name it TransactionContext.jsx
and add the following code:
Here in this code above, we define a React context provider called TransactionsProvider
that manages interactions with our blockchain contract using the Ethereum blockchain and MetaMask.
Firstly, we imported necessary libraries and modules, including React, useEffect, useState for managing component state, and the ethers library for interacting with the Ethereum blockchain.
We also imported contractABI
and contractAddress
from the constant
module we defined earlier.
We created a context named TransactionContext
using React.createContext(). This context will provide the necessary data and functions related to transactions to its consumers.
We define a createEthereumContract
function. This initializes the Ethereum contract using the contractAddress
and contractABI
. It uses the MetaMask
provider to obtain a signer and interact with the contract.
We define several state variables:
formData:
Tracks form data such as the recipient's address, amount, keyword, and message.
currentAccount:
Stores the current Ethereum account's address.
isLoading:
Indicates whether a transaction is currently being processed.
transactionCount
: Stores the number of transactions.
transactions:
Holds an array of structured transaction objects.
For our Data Fetching and Processing, we defined several functions:
Uses the contract instance to fetch all transactions from the blockchain, processes the data, and updates the transactions state.
getAllTransactions:
checkIfWalletIsConnect:
Checks if MetaMask
is installed, gets the user's Ethereum accounts, and updates the currentAccount
state. It then calls getAllTransactions
.
checkIfTransactionsExists:
Initializes the transactionCount
using the contract's method to get the count of transactions.
Wallet Connection and Transaction Submission:
connectWallet:
This function requests user account access from MetaMask
and updates the currentAcc
ount`.
sendT
ransaction: Constructs and sends an
Ethereum` transaction using MetaMask.
It also interacts with the smart contract to add the transaction to the blockchain. It updates the transactionCount
after the transaction is successful.
We use the useEffect
hook is used to automatically check for wallet connection and existing transactions when the transactionCount
changes. This helps keep the UI in sync with the blockchain.
The TransactionsProvider
component wraps its children in the TransactionContext.Provider
. It exposes various state variables and functions related to transactions to its children.
Since we are using the ethers
library, you will need to install it for our application to work properly.
Navigate to your client directory, open your command-line and run this command to install ethers
:
npm i ethers
To start your server again by running the command: npm run dev
We will also wrap our application with a custom context provider. This will provide the context data and functions related to Ethereum
transactions to all components rendered within it.
Update your main.jsx
file with the following code:
Here in this code, we added the
TransactionsProvider
by importing it from the TransactionContext
in the context folder.
The TransactionsProvider
is a custom context provider that wraps our main App
component. It provides the context data and functions for Ethereum
transactions.
We want to add a custom hook to fetch GIFs images from the Giphy API
.
Inside your src
folder, create a new folder, name it hooks
. Inside the hooks folder add a file. Name it useFetch.jsx
and add the following code:
Here in this code, we define a custom React hook named useFetch
that is used to fetch GIFs
from the Giphy API
based on a provided keyword.
It uses the useState
and useEffect
hooks to manage the state and handle the asynchronous fetching of GIFs
.
Now we will be adding our transactions
component logic. Inside the Transactions.jsx file in the components folder, add the following code:
Here in this code, we define two components: TransactionsCard
and Transactions
. We use these components to display a list of transactions with associated information, including GIFs
fetched based on keywords.
We imported the useContext
from the "react" library. The TransactionContext is imported from "../context/TransactionContext".
We imported the useFetch
custom hook from "../hooks/useFetch".
We also imported the dummyData
and shortenAddress
functions from their respective paths.
We define TransactionsCard
functional component which takes various props (addressTo, addressFrom, timestamp, message, keyword, amount, url) to render a transaction card.
We use the useFetch
hook to fetch GIFs based on the keyword prop.
We define the Transactions
functional component which displays a list of transaction cards using the TransactionsCard
component.
It uses the useContext
hook to access the transactions array and currentAccount
from the TransactionContext
.
Depending on whether there is a currentAccount
, it displays a message suggesting to connect an account or showing the "Latest Transactions" header.
The list of transactions is rendered using the map function, combining the dummyData
(predefined transactions) and the dynamic transactions from context.
Now let's update our Welcome
component to add the logic for connectWallet
, handleSubmit
and handleChange
functions we had earlier defined.
Inside the Welcome.jsx
file in the components folder, update it with the following code:
Here in this code above we updated the handleSubmit
function in our previous code by using destructuring to extract values from the formData
object.
The formData object holds data the users inputed in the form.
The values we extracted are addressTo, amount, keyword, and message, which represent the recipient's address, transaction amount, GIF keyword, and message respectively.
We prevented the default behavior of the form submission event by using e.preventDefault();
.
if (!addressTo || !amount || !keyword || !message) return;
This is a conditional statement that checks whether any of the extracted values (i.e., addressTo, amount, keyword, message) are falsy.
If any of these values are falsy (empty or not provided), the condition evaluates to true, and the code inside the if-block doesn't run. This effectively stops the function from continuing if any required field is missing.
If all the required fields (addressTo, amount, keyword, message) have valid values, the sendTransaction
function is invoked.
This function initiates the process of sending a cryptocurrency transaction based on the user's input data.
We will also add the code for the Loader
component. Inside the Loader.jsx
file we have created earlier, add the following code:
Here in this code, we define an arrow function component named
Loader
. This is a simple circular loading animation.
When this component is rendered in our application, it will display a spinning circle with a red border at the bottom. It's often used to show that some asynchronous operation (like data fetching) is in progress.
Connecting application to the blockchain
Now let's connect our application to the blockchain. We will be writing a solidity contract that keeps track of all the transactions that went through the blockchain.
Let's add the dependencies for our smart contract.
Switch to your smart_contract
folder and from your project in your command-line, run the following command:
npm i hardhat @nomiclabs/hardhat-waffle @nomiclabs/hardhat-ethers chai ethereum-waffle ethers
Hardhat is an Ethereum development environment. It enables us to easily deploy contracts, run tests, and debug Solidity code without dealing with live environments. You can read more on their documentation Hardhat Doc
After the dependencies have finished installing, run the command:
npx hardhat
and follow the instructions. Press yes to accept the questions. New files and folders are added to your project directory. You can run an initial test by running the command
npx hardhat test
Also, ensure you install the solidity extension on your Vscode. This would make your writing of solidity code easier.
here is it.
Now, let's write our smart contract for our transactions.
Inside the contracts
folder in your smart_contract
directory, add a file called Transactions.sol
. Inside it add the following code below:
Here in this code,
pragma solidity ^0.8.0;
we use the pragma
directive that specifies the version of the Solidity compiler that should be used.
The caret (^) symbol means the code is compatible with compiler versions greater than or equal to 0.8.0.
contract Transactions {
}
Then, we define a Solidity smart contract named Transactions
uint256 transactionCount;
Then we declare a state variable transactionCount
of type uint256, which will be used to keep track of the number of transactions.
event Transfer(address from, address receiver, uint amount, string message, uint256 timestamp, string keyword);
Then we define an event named Transfer
. Events are simply a way for contracts to communicate that something has happened.
This event is used to log transaction details, such as sender, receiver, amount, message, timestamp, and keyword.
struct TransferStruct {
address sender;
address receiver;
uint amount;
string message;
uint256 timestamp;
string keyword;
}
Then we define a structure named TransferStruct
which holds the components of a transaction. It includes the sender's and receiver's addresses, the transaction amount, a message, timestamp, and a keyword associated with the transaction
TransferStruct[] transactions;
We then declare a dynamic array named transactions that will store instances of TransferStruct
, representing the recorded transactions.
function addToBlockchain(address payable receiver, uint amount, string memory message, string memory keyword) public {
transactionCount += 1;
transactions.push(TransferStruct(msg.sender, receiver, amount, message, block.timestamp, keyword));
emit Transfer(msg.sender, receiver, amount, message, block.timestamp, keyword);
}
Then we added an addToBlockchain
function. This would allow a user to add a new transaction to the blockchain.
What it simply does is it increments the transactionCount
, creates a new instance of TransferStruct
with the provided parameters, pushes it to the transactions array, and emits the Transfer event to log the transaction details.
function getAllTransactions() public view returns (TransferStruct[] memory) {
return transactions;
}
Here, we define a function, getAllTransactions
, which is a view function that returns the entire array of recorded transactions when called. It doesn't modify the contract's state.
function getTransactionCount() public view returns (uint256) {
return transactionCount;
}
And lastly, we define a function, getTransactionCount
, which is also a view function that returns the current value of transactionCount.
From deploy - When we run the deploy code, we are going to get the address of our smart contract deployed on the blockchain network
For our contract to be deployed, we need to have Ethereum on our own wallet already because everything on the blockchain network requires what we know as gas.
To get a wallet account signup for the Metamask account and use it on your browser as an extension.
Metamask is an extension to accessing Ethereum-enabled distributed applications or Dapps in your browser. You can install it here - https://metamask.io/download/
To deploy our smart contract, we will be using Alchemy. Its a platform that would enable us deploy our smart contract. You can signup for an account Here
To get a free Ethereum to test our contract, you can get from the ropsen test network or from goerli faucet - https://goerlifaucet.com/
When you have created Alchemy account, it should take you to the dashboard like this:
Click on Apps Menus on the Dashboard and click on the create App button to create an App. See below:
Writing code to deploy your application.
Top comments (0)