What you’ll be building: see a live and Git Repo . Remember, the online demo uses the ropsten test network. demo Here Introduction Following part one of this tutorial, we will be building the frontend side of this project. If you haven’t seen the , I recommend you do it for the sake of understanding this part-two. PART-ONE If you are ready, let’s crush this app… Project Setup Make sure you already have installed on your machine, if you don’t, follow the link below to do that. NodeJs Jump into your projects directory and create a new folder called “dalto”. You can name it whatever you want, but for the sake of uniformity, I suggest you flow with me on the namings. Within this directory, create two more folders called and , our ethereum code will live in the smart_contract folder, while the react app will live in the client directory. Lastly, open this project in your code editor, I prefer . If you did all that correctly, your project structure should look like this. dalto client smart_contract VS Code The codes should be structured in the following way. Project Structure The Smart Contract Setup Jump into the terminal, into the directory and run the command below. move (cd) smart_contract npm init -y This will create an file in the root of the folder. To specify the packages to be used for building the smart contract, we will run the code snippet below. npm smart_contract yarn add @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle chai ethereum-waffle ethers hardhat --dev # or npm install -D @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle chai ethereum-waffle ethers hardhat Please note that these packages are listed only in the development environment. After the installation is done, you will have a result similar to mine. Now you need to run the code below to set up in the current folder. hardhat smart_contract yarn hardhat # or npm hardhat You will be prompted with some questions, you should select the following options. Create a basic sample project when asked what you want to do. Specify the current directory when asked for the root folder, this option is prefilled on the terminal for you. Enter when asked if you want to add a and hit enter on your keyboard. 'y' .gitIgnore Once you specify the above options properly, hardhat will automatically generate the project structure for you. Next, head on to the directory >> and rename the file to . Afterward, paste the codes below in it, save and move along with me. Processing the Smart Contract smart_contract contracts Greeter.sol Transactions.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Transactions { address private owner; uint256 transactionCounts; mapping (address => uint) balanceOf; event Transfer(address indexed sender, address indexed receiver, uint256 amount, string remark, uint256 timestamp); struct TransferStruct { address sender; address receiver; uint256 amount; string remark; uint256 timestamp; } TransferStruct[] transactions; constructor() { owner = msg.sender; balanceOf[tx.origin] = msg.sender.balance; } function getOwner() public view returns (address) { return owner; } function sendMoney(address payable receiver, uint256 amount, string memory remark) public returns(bool success) { if (balanceOf[owner] < amount) return false; balanceOf[owner] -= amount; balanceOf[receiver] += amount; transactionCounts += 1; transactions.push( TransferStruct( owner, receiver, amount, remark, block.timestamp ) ); emit Transfer(msg.sender, receiver, amount, remark, block.timestamp); return true; } function getBalance(address addr) public view returns(uint) { return balanceOf[addr]; } function getAllTransactions() public view returns(TransferStruct[] memory) { return transactions; } function getTransactionsCount() public view returns(uint256) { return transactionCounts; } } Head to directory >> and renaming the file to . Afterward, replace the codes below in it. The code snippet below specifies where our smart contract will live on the web. Setting up the Deployment Script smart_contract scripts sample-script.js deploy.js const hre = require('hardhat') const main = async () => { const Transactions = await hre.ethers.getContractFactory('Transactions') const transactions = await Transactions.deploy() await transactions.deployed() console.log('Transactions deployed to:', transactions.address) } const runMain = async () => { try { await main() process.exit(0) } catch (error) { console.error(error) process.exit(1) } } runMain() Fantastic work, at this time you should already have your Rinkeby test network funded and ready for use, if you haven’t, please go back to the of this tutorial, you will find the instruction there. PART-ONE Now it's time to know about … alchemy Deploying to Alchemy Currently, our smart contract can only run on our computer and outsiders can’t connect to it. To make it accessible for everyone at no cost, we will use alchemy for that. Proceed by signing up with them, or if you already have an account. LOG IN Once you are logged in, you will see the dashboard page which gives you access to create a new blockchain application. Click on the and fill in the details you want as seen in the image below, make sure to specify the . Creating an Alchemy App CREATE APP button Rinkeby test network After you have created the app, click on the or view the details button to see the app information. app name Click on the and copy the as seen in the image below. VIEW KEY button HTTP URL Fantastic, now follow the steps as seen in the images below to get your Rinkeby account. Please note, we are not using the regular account address but the private key to that account. Awesome, now go back to >> >> and replace its content with the codes below. Use your own in place of the existing one in the file. VS code smart_contract hardhat.config.js URL require('@nomiclabs/hardhat-waffle') module.exports = { solidity: '0.8.0', networks: { rinkeby: { url: '<YOUR_ALCHEMY_APP_URL_GOES_HERE>', accounts: [ '<YOUR_RINKEBY_ACCOUNT_PRIVATE_KEY_GOES_HERE>', ], }, }, } If you had done all that correctly, we just need to do one more thing before we move on to the frontend part of this project. Let’s deploy this smart contract to Alchemy, run the code below, make sure your terminal is in the directory. smart_contract yarn hardhat run scripts/deploy.js --network rinkeby or npx hardhat run scripts/deploy.js --network rinkeby After the smart contract is deployed successfully to Alchemy, you will have the which you can see highlighted in the image below. smart contract address Please copy and save that address, it will later be used in the directory >> >> file. client utils constants.js Congratulations, you just completed the smart contract deployment, now let’s use it in our frontend application. The Frontend Setup Open the terminal, into the directory, and perform the following instructions. For the frontend, we will use to create our react application, Vite is an awesome toolset that makes the process of creating your frontend application simple. cd client Vite For this build, we will also use Yarn as our primary package manager, it's just so much nicer at installing npm packages. Note, all yarn commands can easily be done with npm as well. You just need to make a small adjustment. Now let’s install Vite if you have not. yarn create vite # or npm init vite@latest You will be prompted to enter your project name, just use “ ”. This will instruct Vite to download the codes into the current directory . Next, you will be prompted for the project name, simply key in “ ” and then select from the list of frameworks available. See the image below for guidance. ./ (client) dalto react After the above executions are done on the terminal, run the command below to install the to be used in our project. npm modules yarn install # or npm install Installing Project Dependencies After the process is done on the terminal, again you now have to install the following packages which our application depends on. yarn add @heroicons/react ethers @faker-js/faker identicon.js react-hooks-global-state # or npm install @heroicons/react ethers @faker-js/faker identicon.js react-hooks-global-state If you have installed those packages, you are amazing, let’s proceed to install the which our application also depends on. tailwind CSS Installing Tailwind CSS Use the commands below to do that. yarn add tailwindcss postcss autoprefixer --dev yarn tailwindcss init # or npm install -D tailwindcss postcss autoprefixer npx tailwindcss init You should have two files at the root of the folder. and , but if you don’t you can create them yourself. client tailwind.config.js postcss.config.js Next, replace the contents of the two files with the following codes. # postcss.config.js const tailwindcss = require('tailwindcss') module.exports = { plugins: [tailwindcss('./tailwind.config.js'), require('autoprefixer')], } # tailwind.config.js module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", ], theme: { extend: {}, }, plugins: [], } Now, replace the content of file at the directory >> with the codes below. index.css client src @tailwind base; @tailwind components; @tailwind utilities; Very well, let’s test the app to see if everything is working right by running the code below. # Running the application yarn dev # or npm run dev If you did everything right, you should have the same result as mine. Epic work so far, let’s start coding. We will proceed by coding up the components. Coding the Components We have four components and we will begin with the Header component. Before we do that, go to the directory >> and create a folder called components. This is where all our components will reside. client src Header Component Create a component called . This component contains the which is used to launch the modal for entering new transactions. See code block below. Header.jsx send button addTransactionCard import ethLogo from '../assets/ethlogo.png' import { setGlobalState } from '../store' const Header = () => { return ( <header className="flex flex-row items-center justify-between drop-shadow-md py-2 px-5 bg-white"> <div className="flex flex-row justify-center items-center cursor-pointer"> <img className="w-6 h-6 object-contain cursor-pointer" src={ethLogo} alt="Etherium Logo" /> <span>Dalto</span> </div> <nav className="flex flex-row justify-center items-center list-none"> <li className="cursor-pointer mr-3 hover:text-blue-500">Pricing</li> <li className="cursor-pointer mr-3 hover:text-blue-500">Docs</li> <li className="cursor-pointer mr-3"> <button onClick={() => setGlobalState('modal', 'scale-100')} className="text-white bg-blue-500 py-2 px-5 rounded-xl drop-shadow-xl border border-transparent hover:bg-transparent hover:text-blue-500 hover:border hover:border-blue-500 focus:outline-none focus:ring" > Send </button> </li> </nav> </header> ) } export default Header Amazing, let’s create the component. AddTransactionCard AddTransactionCard Component Still on the components directory, create another component called , afterward, paste the codes below in it and save. We will use this modal component for creating new transactions. A user can launch it whenever the at the Header component is clicked on. AddTransactionCard.jsx send button import { useGlobalState, setGlobalState } from '../store' import { useState } from 'react' import { sendMoney } from '../shared/Transaction' const AddTransactionCard = () => { const [modal] = useGlobalState('modal') const [connectedAccount] = useGlobalState('connectedAccount') const [address, setAddress] = useState('') const [amount, setAmount] = useState('') const [remark, setRemark] = useState('') const [loading, setLoading] = useState(false) const handleSubmit = () => { if (!address || !amount || !remark) return setLoading(true) sendMoney({ connectedAccount, address, amount, remark }) .then(() => { setGlobalState('transaction', { address, amount, remark }) setLoading(false) setGlobalState('modal', '') resetForm() }) .catch((error) => { setLoading(false) console.log(error) }) } const resetForm = () => { setAddress('') setAmount('') setRemark('') } return ( <div className={`fixed top-0 left-0 w-screen h-screen flex items-center justify-center bg-black bg-opacity-50 transform scale-0 transition-transform duration-300 ${modal}`} > <div className="bg-white rounded-xl w-1/3 h-7/12 p-6"> <div className="flex flex-col"> <div className="flex flex-row justify-between items-center"> <p className="font-semibold text-gray-800">Add a step</p> <button onClick={() => setGlobalState('modal', '')} className="border-0 bg-transparent focus:outline-none" > <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" > <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" ></path> </svg> </button> </div> <div className="flex flex-row justify-between items-center bg-gray-100 rounded-xl p-3 mt-5"> <input className="bg-transparent focus:outline-none w-full" type="text" name="address" placeholder="Address To" onChange={(e) => setAddress(e.target.value)} value={address} /> </div> <div className="flex flex-row justify-between items-center bg-gray-100 rounded-xl p-3 mt-5"> <input className="bg-transparent focus:outline-none w-full" type="number" step={0.0001} name="amount" placeholder="Amount (Eth)" onChange={(e) => setAmount(e.target.value)} value={amount} /> </div> <div className="flex flex-row justify-between items-center bg-gray-100 rounded-xl p-3 mt-5"> <input className="bg-transparent focus:outline-none w-full" type="text" name="remark" placeholder="Remark" onChange={(e) => setRemark(e.target.value)} value={remark} /> </div> <div className="flex flex-row justify-between items-centerrounded-xl mt-5"> {!loading ? ( <button type="submit" onClick={handleSubmit} className="flex flex-row justify-center items-center w-full text-white text-lg bg-blue-500 py-2 px-5 rounded-xl drop-shadow-xl border border-transparent hover:bg-transparent hover:text-blue-500 hover:border hover:border-blue-500 focus:outline-none focus:ring" > Send Money </button> ) : ( <button className="flex flex-row justify-center items-center w-full text-white text-lg bg-blue-300 py-2 px-5 rounded-xl drop-shadow-xl border border-transparent focus:outline-none focus:ring" disabled > <svg xmlns="http://www.w3.org/2000/svg" width="30px" height="30px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" > <path d="M10 50A40 40 0 0 0 90 50A40 42 0 0 1 10 50" fill="white" stroke="none" > <animateTransform attributeName="transform" type="rotate" dur="1s" repeatCount="indefinite" keyTimes="0;1" values="0 50 51;360 50 51" ></animateTransform> </path> </svg> Sending... </button> )} </div> </div> </div> </div> ) } export default AddTransactionCard Nice, let create the rest of the components. Hero Component Create another component with the name in the components folder. This component contains the descriptions of what this application does. It has no special functionality but assists the beauty of our app design. Paste the codes below in it and save. Hero.jsx import { LightningBoltIcon, ScaleIcon } from '@heroicons/react/outline' const Hero = () => { const features = [ { name: 'No hidden fees', description: 'Sending money is free of charge, you have no need for a middle man or annoying taxes.', icon: ScaleIcon, }, { name: 'Transfers are instant', description: 'You do not have to wait for days anymore, you can get you money in seconds within any country in the world', icon: LightningBoltIcon, }, ] return ( <div className="py-12 bg-white"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="lg:text-center"> <p className="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-4xl"> A better way to send money </p> <p className="mt-4 max-w-2xl text-xl text-gray-500 lg:mx-auto"> Explore the crypto world. Buy and sell cryptocurrencies easily on Dalto. </p> </div> <div className="mt-10"> <dl className="space-y-10 md:space-y-0 md:grid md:grid-cols-2 md:gap-x-8 md:gap-y-10"> {features.map((feature) => ( <div key={feature.name} className="relative"> <dt> <div className="drop-shadow-xl absolute flex items-center justify-center h-12 w-12 rounded-md bg-blue-500 text-white"> <feature.icon className="h-6 w-6" aria-hidden="true" /> </div> <p className="ml-16 text-lg leading-6 font-medium text-gray-900"> {feature.name} </p> </dt> <dd className="mt-2 ml-16 text-base text-gray-500"> {feature.description} </dd> </div> ))} </dl> </div> </div> </div> ) } export default Hero Lastly, let’s create the Tabular component. Tabular Component Create a component with the name in the components folder and paste the codes below in it. This component is responsible for rendering all the transactions recorded in our blockchain network. Observe the code below. Tabular.jsx import { useEffect, useState } from 'react' import ethLogo from '../assets/ethlogo.png' import Identicon from 'identicon.js' import faker from '@faker-js/faker' import { getAllTransactions } from '../shared/Transaction' import { useGlobalState } from '../store' const Tabuler = () => { const [transactionsStore] = useGlobalState('transactions') const [transactionCount] = useGlobalState('transactionCount') const [transactions, setTransaction] = useState([]) const [start, setStart] = useState(0) const [end, setEnd] = useState(6) const makeImage = (address) => { const data = new Identicon(address, 400).toString() return `data:image/png;base64,${data}` } const loadMoreTransactions = () => { setTransaction((prevState) => [ ...prevState, ...transactionsStore.slice(start, end), ]) setStart(end) setEnd(end * 2) } const shortenAddress = (address) => `${address.slice(0, 5)}...${address.slice(address.length - 4)}` useEffect(() => { getAllTransactions().then((data) => { setTransaction([...data.slice(start, end)]) setStart(end) setEnd(end * 2) }) }, []) return ( <> <section className="antialiased rounded-xl text-gray-600 p-5"> <div className="flex flex-col justify-center h-full"> <div className="max-w-full mx-auto px-4 sm:px-6 lg:px-8 bg-white shadow-2xl rounded-xl"> <header className="px-5 py-4"> <h2 className="font-semibold text-gray-800 text-center"> Total Transactions({transactionCount}) </h2> </header> <div className="p-3"> <div className="overflow-x-auto"> <table className="table-auto w-full"> <thead className="text-xs font-semibold uppercase text-gray-400 bg-gray-50"> <tr> <th className="p-2 whitespace-nowrap"> <div className="font-semibold text-left">Name</div> </th> <th className="p-2 whitespace-nowrap"> <div className="font-semibold text-left">Sender</div> </th> <th className="p-2 whitespace-nowrap"> <div className="font-semibold text-left">Receiver</div> </th> <th className="p-2 whitespace-nowrap"> <div className="font-semibold text-left">Amount</div> </th> <th className="p-2 whitespace-nowrap"> <div className="font-semibold text-left">Timestamp</div> </th> <th className="p-2 whitespace-nowrap"> <div className="font-semibold text-center">Remark</div> </th> </tr> </thead> <tbody className="text-sm divide-y divide-gray-100"> {transactions.map((tx, index) => ( <tr key={index + 1}> <td className="p-2 whitespace-nowrap"> <div className="flex items-center"> <div className="w-10 h-10 flex-shrink-0 mr-2 sm:mr-3"> <img className="rounded-full" src={makeImage(tx.sender)} width="40" height="40" alt="Alex Shatov" /> </div> <div className="font-medium text-gray-800"> {faker.name.findName()} </div> </div> </td> <td className="p-2 whitespace-nowrap"> <div className="text-left"> <a href={`https://ropsten.etherscan.io/address/${tx.sender}`} target="_blank" rel="noreferrer" className="hover:text-blue-500" > {shortenAddress(tx.sender)} </a> </div> </td> <td className="p-2 whitespace-nowrap"> <div className="text-left"> <a href={`https://ropsten.etherscan.io/address/${tx.receiver}`} target="_blank" rel="noreferrer" className="hover:text-blue-500" > {shortenAddress(tx.receiver)} </a> </div> </td> <td className="p-2 whitespace-nowrap"> <div className="flex flex-row justify-center items-center text-left font-medium"> <img className="w-3 h-3 object-contain cursor-pointer mr-1" src={ethLogo} alt="Etherium Logo" /> <span className="text-green-500">{tx.amount}</span> </div> </td> <td className="p-2 whitespace-nowrap"> <div className="text-sm text-center"> {tx.timestamp} </div> </td> <td className="p-2 whitespace-nowrap"> <div className="text-sm text-center">{tx.remark}</div> </td> </tr> ))} </tbody> </table> </div> </div> </div> </div> </section> <div className="text-center mt-5 mb-10"> <button onClick={loadMoreTransactions} className="text-white bg-blue-500 py-2 px-5 rounded-xl drop-shadow-xl border border-transparent hover:bg-transparent hover:text-blue-500 hover:border hover:border-blue-500 focus:outline-none focus:ring" > Load more </button> </div> </> ) } export default Tabuler All these components are unified by a single store of data using the npm package. Now let’s bring together the above components into the component. react-hooks-global-state App The App Component The codes below get all the components united and working together. import { useEffect } from 'react' import AddTransactionCard from './components/AddTransactionCard' import Header from './components/Header' import Hero from './components/Hero' import Tabuler from './components/Tabuler' import { isWallectConnected, checkIfTransactionExist, connectWallet, } from './shared/Transaction' import { useGlobalState } from './store' const App = () => { const [connectedAccount] = useGlobalState('connectedAccount') useEffect(() => { isWallectConnected() checkIfTransactionExist() }, []) return ( <div className="flex flex-col min-h-screen"> <Header /> <Hero /> {!connectedAccount ? ( <div className="text-center mb-10"> <button onClick={connectWallet} className="text-white bg-blue-500 py-2 px-5 rounded-xl drop-shadow-xl border border-transparent hover:bg-transparent hover:text-blue-500 hover:border hover:border-blue-500 focus:outline-none focus:ring" > Connect Wallet </button> </div> ) : ( <> <Tabuler /> <AddTransactionCard /> </> )} </div> ) } export default App Cool, the above code joins all our components together, but what about the state manage? How is it coupled together? Let’s look at the store setup. react-hooks-global-state The Data Store Goto the >> directory and create a folder called store. Inside this store folder create a file called and paste the codes below in it. client src index.jsx import { createGlobalState } from 'react-hooks-global-state' const { setGlobalState, useGlobalState } = createGlobalState({ modal: '', connectedAccount: '', transactions: [], transaction: { address: '', amount: '', remark: '', }, transactionCount: localStorage.getItem('transactionCount'), }) export { useGlobalState, setGlobalState } Nice, this simple state management package takes away all the complexities of Redux or the Context API. This is where we store all our transactions and keep track of the connected account. If you’ve gotten up to this point, you deserve a cup of coffee, let’s work on the next part. The Application Utilities Head to the folder >> directory and create a new folder called . Now, inside of this utils folder create two files called and . The file contains the Application Binary Interface ( ) which was generated by hardhat and the file will prepare it for exports. client src utils constants.js Transactions.json JSON ABI js The ABI is generated by hardhat after compilation, it describes our smart contract and prepares it in a way it can be understood by . ethers.js On the directory goto >> >> >> >> . You will copy the entire codes in this file and paste them in >> >> >> . smart_contract artifacts contracts Transactions.sol Transactions.json client src utils Transactions.json Next, paste the code below into the constants.js file. import abi from './Transactions.json' export const contractAbi = abi.abi export const contractAddress = '<YOUR_DEPLOYED_SMART_CONTRACT_ADDRESS_GOES_HERE>' Awesome, I know this has been intense, but be cool, we are almost finishing up. The Smart Contract Resources This file provides us with all the methods available in the file. These methods will help us communicate with the blockchain app using the library and the URL we copied from Alchemy. Transactions.sol ethers.js Create a folder named within directory >> Create a file named and paste the codes below in it. shared client src. Transaction.jsx import { ethers } from 'ethers' import { setGlobalState } from '../store' import { contractAbi, contractAddress } from '../utils/constants' const { ethereum } = window const getEtheriumContract = () => { const provider = new ethers.providers.Web3Provider(ethereum) const signer = provider.getSigner() const transactionContract = new ethers.Contract( contractAddress, contractAbi, signer ) return transactionContract } const isWallectConnected = async () => { try { if (!ethereum) return alert('Please install Metamask') const accounts = await ethereum.request({ method: 'eth_accounts' }) if (accounts.length) { setGlobalState('connectedAccount', accounts[0]) } else { console.log('No accounts found.') } } catch (error) { console.log(error) throw new Error('No ethereum object.') } } const checkIfTransactionExist = async () => { try { const transactionContract = getEtheriumContract() const transactionCount = await transactionContract.getTransactionsCount() window.localStorage.setItem('transactionCount', transactionCount) } catch (error) { console.log(error) throw new Error('No ethereum object.') } } const connectWallet = async () => { try { if (!ethereum) return alert('Please install Metamask') const accounts = await ethereum.request({ method: 'eth_requestAccounts' }) setGlobalState('connectedAccount', accounts[0]) } catch (error) { console.log(error) throw new Error('No ethereum object.') } } const sendMoney = async ({ connectedAccount, address, amount, remark }) => { try { if (!ethereum) return alert('Please install Metamask') const transactionContract = getEtheriumContract() const parsedAmount = ethers.utils.parseEther(amount) await ethereum.request({ method: 'eth_sendTransaction', params: [ { from: connectedAccount, to: address, gas: '0x5208', value: parsedAmount._hex, }, ], }) const transactionHash = await transactionContract.sendMoney( address, parsedAmount, remark ) console.log(`Loading - ${transactionHash.hash}`) await transactionHash.wait() console.log(`Success - ${transactionHash.hash}`) const transactionCount = await transactionContract.getTransactionsCount() setGlobalState('transactionCount', transactionCount.toNumber()) window.location.reload() } catch (error) { console.log(error) throw new Error('No ethereum object.') } } const getAllTransactions = async () => { try { if (!ethereum) return alert('Please install Metamask') const transactionContract = getEtheriumContract() const availableTransactions = await transactionContract.getAllTransactions() const structuredTransactions = availableTransactions.map((tx) => ({ receiver: tx.receiver, sender: tx.sender, timestamp: new Date(tx.timestamp.toNumber() * 1000).toLocaleString(), remark: tx.remark, amount: parseInt(tx.amount._hex) / 10 ** 18, })).reverse() setGlobalState('transactions', structuredTransactions) return structuredTransactions } catch (error) { console.log(error) throw new Error('No ethereum object.') } } export { getEtheriumContract, isWallectConnected, checkIfTransactionExist, connectWallet, sendMoney, getAllTransactions, } If you are confused about what the above functions do, please consult the of this tutorial here. PART-ONE Download and place the following images in the directory >> >> and your done. client src assets https://raw.githubusercontent.com/Daltonic/dalto/main/client/src/assets/ethLogo.png https://github.com/Daltonic/dalto/blob/main/client/src/assets/logo.png?raw=true Great, you just crushed the entire application, its time to test it out, run the code below. yarn dev # or npm run dev Conclusion Congratulations on completing a full-fledge decentralized application with react and solidity. Building a web3.0 app can be challenging, being that it demands a lot of skills and components, but it's not impossible. Hopefully, the knowledge you gained from this tutorial has helped in some way. Please leave a handclap, or click on the like button to show some love. Thanks for coding along, see you in the … next tutorial About the Author Gospel Darlington kick-started his journey as a software engineer in 2016. Over the years, he has grown full-blown skills in JavaScript stacks such as React, ReactNative, VueJs, and more. He is currently freelancing, building apps for clients, and writing technical tutorials teaching others how to do what he does. Gospel Darlington is open and available to hear from you. You can reach him on , , , or on his . LinkedIn Facebook Github website Also published on: https://dev.to/daltonic/building-an-ethereum-transaction-app-with-react-and-solidity-part-two-2pg2