DEV Community

Cover image for Building SendCrypto: A Web 3.0 Application for Secure Blockchain Transactions
James Oyanna
James Oyanna

Posted on • Updated on

Building SendCrypto: A Web 3.0 Application for Secure Blockchain Transactions

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
Send crypto

Ethereum card

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:

Vite landing page

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.

Initial page

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:

Navbar

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 handleChangefunction. 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.

Welcome page

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-iconspackage.

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 Ethereumsmart 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:

getAllTransactions:
Uses the contract instance to fetch all transactions from the blockchain, processes the data, and updates the transactions state.

checkIfWalletIsConnect: Checks if MetaMask is installed, gets the user's Ethereum accounts, and updates the currentAccountstate. 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 currentAccount`.

sendTransaction: 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 transactionscomponent 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.jsxfile 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.

Solidity Extension

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;
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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;
}

Enter fullscreen mode Exit fullscreen mode

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:

Alchemy dashboard

Click on Apps Menus on the Dashboard and click on the create App button to create an App. See below:

Create App

Writing code to deploy your application.

Top comments (0)