Real-Time Blockchain Notifications
Real-Time Blockchain Notifications
Shubhankar Banerjee
All rights reserved. No part of this publication may be reproduced, distributed, or transmitted in
any form or by any means, including photocopying, recording, or other electronic or mechanical
methods, without the prior written permission of the publisher, except in the case of brief
quotations embodied in critical reviews and certain other noncommercial uses permitted by
copyright law. Although the author/co-author and publisher have made every effort to ensure
that the information in this book was correct at press time, the author/co-author and publisher do
not assume and hereby disclaim any liability to any party for any loss, damage, or disruption
caused by errors or omissions, whether such errors or omissions result from negligence,
accident, or any other cause. The resources in this book are provided for informational purposes
only and should not be used to replace the specialized training and professional judgment of a
health care or mental health care professional. Neither the author/co-author nor the publisher
can be held responsible for the use of the information provided within this book. Please always
consult a trained professional before making any decision regarding the treatment of yourself or
others.
https://www.c-sharpcorner.com/ebooks/ 2
Educational background
Shubhankar completed a Bachelor of Computer Application (BCA) from PSIT College, affiliated
with Chhatrapati Sahu Ji Maharaj University Kanpur, in the year 2021. During his undergraduate
studies, he gained a strong foundation in computer science, including programming languages,
database management, and software development methodologies.
Building upon his undergraduate education, Shubhankar pursued a Master of Computer
Application (MCA) from MPEC College, affiliated with APJ Abdul Kalam University, Lucknow,
graduating in the year 2023. Throughout his master's program, he delved deeper into advanced
topics such as blockchain, artificial intelligence and machine learning. Additionally, he honed his
skills in software engineering principles and system architecture.
These academic pursuits have made him well-prepared to excel in professional roles within the
tech industry.
— Shubhankar Banerjee
https://www.c-sharpcorner.com/ebooks/ 3
Table of Contents:
Introduction to Real-Time Blockchain Notification System ............................................................... 5
Technologies to be Used ............................................................................................................... 7
Setting up your Alchemy App .......................................................................................................10
Building Server Infrastructure and Essential Endpoints ...................................................................16
Extending local server’s reach with ngrok ......................................................................................24
Creating a Webhook and connecting it with server logic .................................................................29
Designing the User-Interface (Part 1) ............................................................................................37
Designing the User-Interface (Part 2) ............................................................................................59
https://www.c-sharpcorner.com/ebooks/ 4
1
Introduction to Real-Time
Blockchain Notification
System
Overview
https://www.c-sharpcorner.com/ebooks/ 5
Real-Time Notifications
Real-time notifications are instant alerts that provide users with immediate updates on critical
events within a system, ensuring timely awareness and engagement. They play a crucial role in
enhancing user experience by delivering information promptly as it unfolds. Real-time
notifications are set to transform the way users stay informed and interact with their activities.
These notifications aren't just alerts they provide user’s immediate insights into their in-app
activities. Real-time notifications have become essential in today's digital landscape where users
crave instant updates from their favorite apps. Think of this just like how your phone buzzes with
notifications from social media, banking apps, or even news alerts. Similarly, real-time
blockchain notifications provide timely updates about your blockchain activities, making your
journey more informed and secure.
https://www.c-sharpcorner.com/ebooks/ 6
2
Technologies to be Used
Overview
https://www.c-sharpcorner.com/ebooks/ 7
Greetings again, readers. As we explore the amazing world of real-time blockchain notifications,
it is critical to understand the key technologies behind this dynamic journey. Let us look at the
key technologies that form the foundation of our real-time notification system.
During our technical journey, we came across important technological components such as
Alchemy Webhooks, Node.js, Express.js, ngrok, MySQL Database, and React.JS. These
technologies serve as the linchpins, orchestrating a symphony of real-time updates and
notifications, each playing a distinct role in ensuring a smooth and dynamic user experience.
Let’s look at these one at a time and understand about them.
Key Features
• Event Capture: Alchemy Webhooks captures real-time events within the blockchain
ecosystem, ranging from transactions to account creations.
• Event Dispatch: Once captured, it swiftly dispatches these events, acting as the catalyst
for timely and dynamic notifications.
• Integration with Alchemy Dashboard: Alchemy Webhooks seamlessly integrates with
the Alchemy Dashboard, offering a user-friendly interface for developers to manage and
configure their webhook settings.
• Scalability: Notably, Alchemy Webhooks boasts the ability to monitor an impressive
50,000 addresses concurrently.
Node.js
In our technological infrastructure, Node.js acts as the brainpower behind our operations. It
functions as the engine room, quietly handling server-side JavaScript, the coding language that
governs how our system behaves. In simpler terms, it processes instructions and ensures that
everything in our system runs smoothly. Think of it as the backbone, managing the core
functionalities of our notification system.
Express.js
Express.js serves as the meticulous coordinator of meeting points, commonly referred to as
endpoints. These are designated spots where different actions take place in our system.
Express.js organizes and manages these meeting points efficiently. Imagine it as the event
planner ensuring everything is in its place and runs seamlessly. It works together with Node.js to
make sure our system operates like a well-oiled machine.
https://www.c-sharpcorner.com/ebooks/ 8
Ether.js
We will be Ether.js is a specialized toolkit that plays a pivotal role by bestowing a touch of
enchantment upon our system, enabling seamless communication and interaction with the
Ethereum blockchain. It gives our system the capacity to understand, interact, and work
successfully with the complex language of the blockchain. This skill enables our system to
navigate the complexity of blockchain data with more ease.
To put it simply, ether.js acts as a bridge that allows our system to understand, interpret, and
communicate with the Ethereum blockchain.
using ethers.js package version 5.7.2.
MySQL Database
The database functions as a digital administrator, keeping notifications that will be displayed to
users later. This structured method serves a key purpose: when a user wants to access
previous notifications, the database allows for quick and efficient retrieval.
Consider MySQL Database the unsung hero that secretly protects the integrity of our notification
data. Its job goes beyond simple storage; it serves as the backbone, ensuring that alerts are
safely stored for future reference.
Summary
As we wrap up this chapter on the technologies steering your real-time blockchain notifications,
let's reflect on the powerhouses we've introduced – Alchemy Webhooks, Node.js, Express.js,
Ether.js, Ngrok, MySQL Database, and React.js. They're the backbone of your notification
system, working seamlessly to make your blockchain experience cutting-edge.
But hold on tight because the journey doesn't stop here. We're gearing up for the next chapter, a
deep dive into Alchemy. It's where we roll up our sleeves, sign up, and integrate Alchemy into
your blockchain applications. The stage is set for Chapter 3: Setting Up Alchemy - A Deep Dive.
We're not just scratching the surface; we're diving deep into the heart of blockchain integration.
Are you ready for the next leg of this thrilling journey? Let's keep the momentum going!
https://www.c-sharpcorner.com/ebooks/ 9
3
Setting up your Alchemy
App
Overview
https://www.c-sharpcorner.com/ebooks/ 10
Welcome back, developers! In this chapter, we'll take a deep dive into Alchemy, the powerhouse
that simplifies the intricacies of working with blockchain, paving the way for seamless
development. We already learnt about Alchemy and Alchemy webhooks, but in this chapter we
will look at them in further depth. Let's look at some of the key words and steps you'll need to
get started with Alchemy and create an App.
Understanding Alchemy
Alchemy Web3 is a powerful platform that makes it easy for developers to build and interact with
blockchain applications. It acts as a bridge between our code and the blockchain network,
making the development process smoother and more efficient. It acts like a middleware for our
smart contract code and the blockchain network.
Today blockchain technology is revolutionizing how we handle transactions and data. Alchemy
Web3 steps in by simplifying the complexities of working with blockchain. This allows developers
to focus on creating their decentralized applications (dApps) without getting stuck down in the
technical details.
https://www.c-sharpcorner.com/ebooks/ 11
Step 2. Sign Up in Alchemy
Sign Up and create an account in Alchemy directly using your Google account or any other
email, as per your wish.
https://www.c-sharpcorner.com/ebooks/ 12
Step 4. Open apps tab
Click on the Apps button in the navbar on the left side to access the Apps window.
https://www.c-sharpcorner.com/ebooks/ 13
Follow the prompts in the pop-up window, selecting the Chain and Network, assigning a unique
name, and providing a brief description.
Click on the Create app button to proceed.
Here, in this demo, I will be creating an app for the Polygon Testnet blockchain network and
name the app "Polygon_Mumbai".
https://www.c-sharpcorner.com/ebooks/ 14
Congratulations! You've successfully forged an RPC URL, connecting you to the Polygon
Mumbai Testnet.
Repeat this process to create RPC URLs for different blockchain networks. But remember, a
maximum of 5 apps can be created on Alchemy.
Summary
In this chapter, we explored Alchemy's role as a powerhouse in simplifying blockchain
development. We learned how Alchemy Web3 acts as a bridge, freeing developers from
technical complexities and empowering them to focus on innovation. The Alchemy RPC URL
emerged as a key to seamless blockchain interactions.
Practically, we navigated the steps of signing up with Alchemy, creating our first app, and
obtaining the RPC URL for Polygon Mumbai Testnet. As we conclude, the journey continues in
Chapter 4, where we dive into building a Node.js server. Don't miss the chance to enhance your
skills and explore the intricacies of blockchain account and notification management. Join us in
the next chapter for a hands-on experience!
https://www.c-sharpcorner.com/ebooks/ 15
4
Building Server
Infrastructure and Essential
Endpoints
Overview
https://www.c-sharpcorner.com/ebooks/ 16
Welcome back, Developers! In our last chapter, we explored the important role of Alchemy in
simplifying blockchain development. Now, we focus on Node.js, the backbone of our real-time
notification system. n system. This chapter is dedicated to constructing our server and
establishing the essential endpoints that will play a pivotal role in shaping our future
developments.
Prerequisites
Before we embark on our Node.js journey, let's ensure we have the essentials in place. Setting
up a development environment is a breeze with the right tools. Here's a quick overview:
Node.js Installation:
• Head over to nodejs.org.
• Click the LTS version for most users and follow the on-screen instructions to install
Node.js on your device.
mkdir BlockchainNotifier_Server
cd BlockchainNotifier_Server
Congratulations! You've got your project ready with the name BlockchainNotifier_Server.
https://www.c-sharpcorner.com/ebooks/ 17
npm init -y //Initialize a node server
This command initializes a package.json file, which is like a project ID card. The -y flag saves us
from answering a bunch of questions making the process quick and easy.
const PORT = 3000; //define the PORT on which the server has
to run
app.listen(PORT, () => {
console.log(`Server is running at http://localhost:${PORT}`);
//running script
});
node server.js
https://www.c-sharpcorner.com/ebooks/ 18
• Getting Webhook response and storing it in the database
• Endpoint to fetch notification.
Let’s create these endpoints one at a time.
Endpoint:
let newWallet = {
"message": "Success",
"Mnemonic": wallet.mnemonic.phrase.toString(),
"Address": wallet.address.toString(),
"PrivateKey": wallet.privateKey.toString()
}
response.send(newWallet);
}
catch {
result = {
"message": "error"
}
response.send(result)
}
});
Here, we are defining an endpoint named “createAccount” whenever a client endpoint hits this
endpoint. A JSON object containing wallet address, private key and passphrase will be returned.
Using this endpoint we can create new Ethereum wallets.
The createAccount endpoint uses a predefined method present in the ethers package named
createRandom to generate random wallets.
https://www.c-sharpcorner.com/ebooks/ 19
Endpoint:
let networkDetails = {
"message": "Success",
https://www.c-sharpcorner.com/ebooks/ 20
"balance": balanceInEthers.toString()
};
response.send(networkDetails);
}
catch {
result = {
"message": "error"
}
response.send(result)
}
});
Here, we are defining the endpoint with the name “/balance” which takes two parameters RPC
URL of the network on which balance is to be checked and wallet address whose balance is to
be checked.
Pre-requisites
• Your device should have MySQL database installed.
• Create a database with any name you desire and, in that database, create an empty
table with columns such as notification_Id (type: integer), userAddress (type: varchar),
notification_msg (type: varchar), isRead (type: boolean).
Once you are ready with the database, then let’s connect the server with the database.
https://www.c-sharpcorner.com/ebooks/ 21
}
console.log('Connected to MySQL database');
});
Here, we are providing the connection details for our database and then connecting using the
connection string.
We also need to enable our app to use JSON parser middleware to parse the JSON which will
be provided by the webhook inside the request body.
Now, we are ready to define an endpoint where we will be receiving the webhook data.
Endpoint:
Here, an endpoint “/webhook” is defined to receive the JSON data from the Alchemy Webhook
whenever a transaction occurs on a particular address provided in the webhook.
In this, we are taking the user address from the JSON object withthe key toAddress. It means
that the wallet address who will be in the receiving end will get the notification generated, and
the notification msg is created using the amount and sender’s address.
After that, I insert the generated notification into the notification table that is present in my
database.
https://www.c-sharpcorner.com/ebooks/ 22
After connecting the server app with the database and storing the notifications, it’s now time to
fetch those notifications on the basic of the user address.
Endpoint:
res.send(finalResult);
});
});
Here, we are defining an endpoint with the name “/notification” which takes a parameter
userAddress and fetches the notification from the database based on userAddress.
Summary
In this chapter, we harnessed the power of Node.js to construct a robust server for our real-time
blockchain notification system. Essential endpoints were created, enabling functionalities like
wallet creation, account recovery, balance fetching, webhook response processing, and
notification retrieval. The practical guide empowers developers to implement these features
seamlessly. As we conclude, dive into Chapter 5, where we extend the system's reach with
ngrok for global accessibility. The journey continues -- happy coding!
https://www.c-sharpcorner.com/ebooks/ 23
5
Extending local server’s
reach with ngrok
Overview
https://www.c-sharpcorner.com/ebooks/ 24
Welcome back, Developers! In our last chapter, we learned how to start our own Node.js server
to capture real-time events from the blockchain. We also created the important endpoints that
we will need in the project. Lastly, we also learned how to connect our server with the database.
In this chapter we will be learning how to extend the reach of the local node server that we
created globally, using a handy tool called ngrok. But first let’s look at why we need temporary
hosting.
Step 1: Head over to the ngrok download website from where we will be downloading ngrok -
https://ngrok.com/download.
Step 2: Find the download section, and depending on your operating system, grab the right
version.
Step 3: Once downloaded, unzip the file to reveal the ngrok executable file. There will be a file
with a .exe extension.
Step 4: To check the correct ngrok installation. Open your terminal or command prompt.
Navigate to the folder where you unzipped ngrok and type ngrok. Some basic information should
pop up on your screen.
https://www.c-sharpcorner.com/ebooks/ 25
Step 5: Add the auth-token to the default ngrok.yml file. To do that in the same terminal run the
command.
Step 2: Navigate to the directory where your Node.js server file is located.
Step 3: Make sure your Node server is running – if not, start it up with node [your_server_file.js].
Step 5: Navigate to the folder where your ngrok executable file is present.
Step 6: Type - ngrok http [your_local_port] (replace [your_local_port] with your actual local
server port, for eg.,3000 for Node.js).
Step 7: Congratulations! Ngrok now generates a public URL for your local server. Use that URL
to access your server and server-side APIs.
https://www.c-sharpcorner.com/ebooks/ 26
With this, whenever there is a request on the ngrok server it gets forwarded to your local host
server and the response is then returned to the user.
Single User
Only one user can access the free plan making it great for personal projects but not ideal for
large-scale performances.
https://www.c-sharpcorner.com/ebooks/ 27
TCP connection rate of 120 per minute signifies that users can establish a maximum of 120
Transmission Control Protocol (TCP) connections per minute. This means only 120 connections
can be made per minute.
Summary
In this chapter, we explored the necessity of extending the reach of our local Node.js server
globally for a real-time blockchain notification system. Temporary hosting becomes essential to
showcase these notifications without a long-term commitment. Ngrok, our tool of choice, acts as
a virtual stage, making our local server globally visible. The chapter covers the installation and
configuration of ngrok, emphasizing the importance of a secure authentication token.
Readers learn step-by-step procedures, from downloading ngrok to configuring it with their
Node.js server. The process involves running the Node server globally using ngrok, generating a
public URL for global access. The chapter concludes by highlighting the flexibility and simplicity
of ngrok, noting its role as a backstage crew effortlessly setting the stage for global server
exposure.
Moving on to chapter 6, now we will be creating a webhook to track the address activity and
move forward with the notification generation.
https://www.c-sharpcorner.com/ebooks/ 28
6
Creating a Webhook and
connecting it with server
logic
Overview
https://www.c-sharpcorner.com/ebooks/ 29
Welcome back, Developers! In our previous chapter, we set out on a journey to extend the
global reach of our local Node.js server using an effective tool called ngrok. We created a virtual
stage, temporarily hosting our server for the world to witness the real-time blockchain
notifications. Using this we can easily receive the notification for any transaction update for a
particular address.
In this chapter, we look at Alchemy Webhooks, and learn how we can create a webhook using
the Alchemy dashboard as well as API.
Types of Transfers
Address Activity webhook captures three main types of transfers:
Supported Networks
Alchemy's Address Activity webhook supports both Mainnet and Testnet environments across
multiple blockchain networks, ensuring flexibility in development and testing.
https://www.c-sharpcorner.com/ebooks/ 30
Chain Mainnet Testnet
Ethereum
Polygon
Optimism
Arbitrium
Base ; no internal transaction support ; no internal transaction support
Example Response
Upon a tracked address engaging in token or ETH transactions, the webhook provides a
response, detailing the event's essential information. This includes data such as the transaction
block number, hash, involved addresses, transferred value, token details, and more. The
response is provided in a JSON format like -
{
"webhookId": "wh_k63lg72rxda78gce",
"id": "whevt_vq499kv7elmlbp2v",
"createdAt": "2024-01-23T07:42:26.411977228Z",
"type": "ADDRESS_ACTIVITY",
"event": {
"network": "ETH_MAINNET",
"activity": [
{
"blockNum": "0xdf34a3",
"hash":
"0x7a4a39da2a3fa1fc2ef88fd1eaea070286ed2aba21e0419dcfb6d5c5d9f02a72",
"fromAddress": "0x503828976d22510aad0201ac7ec88293211d23da",
"toAddress": "0xbe3f4b43db5eb49d1f48f53443b9abce45da3b79",
"value": 293.092129,
"erc721TokenId": null,
"erc1155Metadata": null,
"asset": "USDC",
"category": "token",
"rawContract": {
"rawValue":
"0x0000000000000000000000000000000000000000000000000000000011783b21",
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"decimals": 6
},
"typeTraceAddress": null,
"log": {
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000503828976d22510aad0201ac7ec88293211d23da",
"0x000000000000000000000000be3f4b43db5eb49d1f48f53443b9abce45da3b79"
https://www.c-sharpcorner.com/ebooks/ 31
],
"data":
"0x0000000000000000000000000000000000000000000000000000000011783b21",
"blockNumber": "0xdf34a3",
"transactionHash":
"0x7a4a39da2a3fa1fc2ef88fd1eaea070286ed2aba21e0419dcfb6d5c5d9f02a72",
"transactionIndex": "0x46",
"blockHash":
"0xa99ec54413bd3db3f9bdb0c1ad3ab1400ee0ecefb47803e17f9d33bc4d0a1e91",
"logIndex": "0x6e",
"removed": false
}
},
{
"blockNum": "0xdf34a3",
"hash":
"0xc84eeeb72d2b23161fd93b088f304902cbd8b4510f1455a65fdac160e37b3173",
"fromAddress": "0x71660c4005ba85c37ccec55d0c4493e66fe775d3",
"toAddress": "0x7853b3736edba9d7ce681f2a90264307694f97f2",
"value": 2400,
"erc721TokenId": null,
"erc1155Metadata": null,
"asset": "USDC",
"category": "token",
"rawContract": {
"rawValue":
"0x000000000000000000000000000000000000000000000000000000008f0d1800",
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"decimals": 6
},
"typeTraceAddress": null,
"log": {
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000071660c4005ba85c37ccec55d0c4493e66fe775d3",
"0x0000000000000000000000007853b3736edba9d7ce681f2a90264307694f97f2"
],
"data":
"0x000000000000000000000000000000000000000000000000000000008f0d1800",
"blockNumber": "0xdf34a3",
"transactionHash":
"0xc84eeeb72d2b23161fd93b088f304902cbd8b4510f1455a65fdac160e37b3173",
"transactionIndex": "0x48",
https://www.c-sharpcorner.com/ebooks/ 32
"blockHash":
"0xa99ec54413bd3db3f9bdb0c1ad3ab1400ee0ecefb47803e17f9d33bc4d0a1e91",
"logIndex": "0x74",
"removed": false
}
}
]
}
}
The response data provided above is a dummy response, which tells us the format and keys
that the response object from webhook will contain.
Now, as we have learned about Address activity webhook, let’s now learn how to create it.
Pre-requisites
• Should have an Alchemy account.
• Should have the blockchain network App created before trying to create the webhook for
that blockchain network.
After ensuring the pre-requisites that are needed, we are set to begin with the creation of
address activity webhook.
We can create address activity webhook using two methods.
Step 1: In your Alchemy Dashboard click the Webhooks tab present on the left navbar.
https://www.c-sharpcorner.com/ebooks/ 33
Step 2: Find Address Activity and click the CREATE WEBHOOK button on the right side of the
window.
Step 3: Select your CHAIN in the dropdown. Then choose your NETWORK. Remember that
there should be an App created for the Chain and Network you choose.
Step 4: Paste your unique webhook URL into the WEBHOOK URL field. In our case it will be
the ngrok global URL that we learned to generate in the previous chapter followed by the
/webhook endpoint as it is the dedicated endpoint, we designed to handle the webhook
response. For example: “ https://cfe7-182-156-224-114.ngrok-free.app/webhook “
Remember, the ngrok URL is temporary and you need to modify the URL for the webhook if it is
closed as every time starting the hosting, will generate new URL.
Step 5: Enter your ETHEREUM ADDRESSES that need to be monitored. Remember that
webhook response will be generated for only those addresses who are added to the Addresses
to be monitored.
Step 6: Click CREATE WEBHOOK. Your webhook is created and appears in the list.
With this we have successfully created a webhook. Now moving forward let’s take a look at how
to create a webhook using Api.
Step 1: Install axios package which will help us to post a request to the Alchemy Servers.
Step 2: Add the requirement for Axios and dotenv package which we have already installed
earlier.
https://www.c-sharpcorner.com/ebooks/ 34
require("dotenv").config();
Step 3: Create a file named dotenv(.env) in your project folder and store your Alchemy
Webhook Token or AlchemyXToken in the env file.
To get the Alchemy Token visit the Webhook page in your Alchemy dashboard which I
mentioned while discussing how to create Webhook using the dashboard interface earlier. There
at the top right corner, you will find your Alchemy Auth-Token
const requestData = {
network: req.body.WebhookNetwork,
webhook_type: "ADDRESS_ACTIVITY",
webhook_url: req.body.WebhookURL,
addresses: req.body.WebhookAddress
}
const response = await
axios.post("https://dashboard.alchemy.com/api/create-webhook/", requestData, {
headers: {
"Accept": "application/json",
"X-Alchemy-Token": XAclechyToken,
"Content-Type": "application/json"
}
});
// Handle success
const responseBody = {
message : "Webhook Created",
response : {
https://www.c-sharpcorner.com/ebooks/ 35
Webhook_Id : response.data.data.id,
}
}
res.status(200).send(responseBody);
} catch (error) {
// Handle errors
res.status(500).json({ error: error.message });
}
});
Here, as webhook_url you need to paste your unique webhook URL. In our case it will be the
ngrok global URL that we learned to generate in the previous chapter followed by the /webhook
endpoint as it is the dedicated endpoint, we designed to handle the webhook response. For
example: “ https://cfe7-182-156-224-114.ngrok-free.app/webhook “.
Provide the Address to be added to the webhook in the form of a string array and the network on
which you want to create the webhook. You can choose whichever network you want and then
use the network name convention that Alchemy supports. You can find the Networks and
naming convention that Alchemy supports at https://docs.alchemy.com/create-webhook
And lastly you will be getting the webhook Id of the newly created Webhook. You can store it in
a database table for future use. Using the Webhook Id you can Add/Remove wallet addresses to
it. With this, we have learned how to create an Address-Activity Webhook using the dashboard
as well as alchemy API.
Summary
In this chapter, we explored the world of Alchemy Webhooks and discovered their critical role in
enabling smooth communication between our Node.js server and the dynamic blockchain.
Alchemy Webhooks' quick event processing and real-time communication features guarantee
that our server remains aware of critical transactions while avoiding excessive data retrieval. We
investigated the dynamic features of the Address Activity webhook, such as tracking
transactions through ETH, ERC20, ERC721, and ERC1155 tokens. The chapter concluded with
a practical tutorial to configure the Address Activity webhook using both the Alchemy Dashboard
and the API. We walked developers through the procedures to ensure they could smoothly
integrate this powerful technology into their blockchain apps.
Moving forward we will continue our exploration further into the realm of user interfaces with the
next chapter in line.
https://www.c-sharpcorner.com/ebooks/ 36
7
Designing the User-
Interface (Part 1)
Overview
https://www.c-sharpcorner.com/ebooks/ 37
Welcome back, Developers! In the previous chapter, we learned about an effective tool to work
as the base for our real-time blockchain notification-Alchemy Webhooks and learned how to set
up these webhooks to receive notifications. With this, we have completed the server side of our
blockchain Notification. But now that doesn't mean that there won’t be a need for any
modifications. Feel free to add new features according to your wish.
In this chapter, we'll look at how we can create user interfaces that work smoothly with our
project. A well-designed interface is more than just an aesthetic element; it serves as the entry
point for a user's engagement with the dynamic world of blockchain transactions.
Blockchain's decentralized and transparent nature requires a user interface that reflects its
innovation. We will be creating a basic User-Interface for you to understand it well, but you are
free to create a more appealing UI/UX as you wish.
By running these commands, a new React-app will be created. In the terminal type
This will start the app and a react template will be displayed in your browser.
https://www.c-sharpcorner.com/ebooks/ 38
Creating First Page
After you have initialized a new project, let’s start creating the first page which should be our
Home page.
Inside the src folder create a new folder named “Components”. Here we will be keeping all our
Components i.e. (JavaScript and CSS files).
Now after creating the file define how the component should look.
The first page should be telling the users about the wallet with 2 buttons, one to create a new
account and other to recover the account in it. The Home_page.js file looks like this:
// Home_page.js
import React from "react";
import logo from '../../Images/yourCryptoLogo-removebg-preview.png';
import ethereumLogo from "../../Images/ethereumLogo.png";
import polygonLogo from "../../Images/polygonLo.png";
import avalancheLogo from "../../Images/avalancheLogo.png";
import solanaLogo from "../../Images/solanaLogo.png";
import '../HomePageComponent/CSS/Home_page.css';
import { useNavigate } from "react-router-dom";
function Home_page() {
function ClearLocalStorage() {
localStorage.clear()
}
return (
<div>
<ClearLocalStorage />
<div className="backcard">
<div className="logo center-align">
https://www.c-sharpcorner.com/ebooks/ 39
<img src={logo} alt='Wallet' className="logoImage" />
</div>
This JS file uses some custom CSS and bootstrap CSS. To add bootstrap CSS in your project,
install the bootstrap npm package.
After installing the bootstrap package add the following line to the src/index.js file inside your
project structure.
You can add additional styling according to your taste using custom css. Create a new file like
Home_page.css and import it inside your JavaScript file and style your page according to your
need.
body {
background-color :#EBEFF2;
max-width: 100%;
max-height: 100%;
overflow: hidden;
}
https://www.c-sharpcorner.com/ebooks/ 40
.backcard {
height : 500px;
width: 420px;
background-color: #ECF0F2;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
box-shadow: 10px 10px 20px #D3DBE7, -0.5rem -0.5rem 20px white;
border-radius: 20px;
}
.center-align {
width: fit-content;
margin: auto;
}
.top-align{
width: 100%;
position: relative;
}
.button-container{
width: 50%;
margin: 1.5rem auto;
display: flex;
flex-direction: column;
}
.set-button{
border-radius: 1px;
width: 13.5rem !important;
border-radius: 16px;
margin-top: 5% auto;
}
.logo {
margin-top: 5px;
}
.logoImage {
width: 15rem;
}
.start-guide-text {
margin-top: 1.2rem !important;
max-width: 70%;
text-align: center;
https://www.c-sharpcorner.com/ebooks/ 41
.color-logo {
font-weight: bold;
color: #7028FF;
}
.support-logoImage {
width: 1.8rem;
margin: 0px 0.5em 0px 0.5em;
}
.support-logo{
display: flex;
margin-top: 1em;
}
With this, we have created our first page for our project. Run the command npm start to view the
page. But wait, you won’t be able to view the page not yet. To view the page, we need to define
the routes of the page. Install a npm package named react-router-dom
After installing react-router-dom, modify the index.js file. Your index.js file should look like -
// src/Index.js File
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
// use BrowserRouter from react-router-dom
import { BrowserRouter } from 'react-router-dom';
// Add the line to use Bootstrap
import 'bootstrap/dist/css/bootstrap.css'
After modifying your index.js for routing service modify your App.js file
https://www.c-sharpcorner.com/ebooks/ 42
// src/App.js
import React from "react";
import HomePage from "./Components/HomePageComponent/Home_page";
function App() {
return (
<switch>
<Routes>
<Route path = "/" element = {<HomePage />}/>
</Routes>
</switch>
);
}
export default App; With this, you are all set to view your first page. Now run your app and view
the first page.
Step 1: Create a new JavaScript (.js) file in the Components folder, naming Create_account.js.
https://www.c-sharpcorner.com/ebooks/ 43
Step 2: Modify the App.js file where we are defining the routes for our project. Here import your
Create_account.js file and then define a route to it.
//Define Routes
//Import the JavaScript file
import CreateWallet from "./Components/WalletActions/Create_wallet"
Step 3: Now in your Create_wallet.js file write your page code, along with functionalities.
//Create_wallet.js
import React, { useEffect, useState } from "react";
import "./CSS/Create_wallet.css"
import logo from '../../Images/yourCryptoLogo-removebg-preview.png';
import Back from '../../Images/87-875958_back-arrow-in-circle-symbol-removebg-
preview.png'
import { useNavigate } from "react-router-dom";
function Create_wallet() {
const [isCopied, setIsCopied] = useState(false);
const [isHidden, setIsHidden] = useState(true);
const [Phrase, setPhrase] = useState('');
const [Privatekey, setPrivatekey] = useState('');
const [Address, setAddress] = useState('');
https://www.c-sharpcorner.com/ebooks/ 44
useEffect(() => {
console.log("here");
// Calling the createAccount endpoint created in the server app
fetch('http://localhost:3000/createAccount')
.then(response => response.json())
.then(data => {
console.log(data);
setPhrase(data.Mnemonic);
setPrivatekey(data.PrivateKey);
setAddress(data.Address);
});
}, [])
function getBack() {
navigate("/")
}
function getNext() {
SaveToLocalStorage();
navigate("/wallet")
}
function SaveToLocalStorage() {
const AccountDetails = [{
AccountName: "Account 0",
Mnemonic: Phrase,
PrivateKey: Privatekey,
Address: Address
}];
let activeNetwork = {
NetworkName: "Polygon-Mumbai",
Network: "Testnet",
RPC_URL: "https://rpc-mumbai.maticvigil.com",
Chain_ID: "80001",
Currency: "MATIC"
}
localStorage.setItem('ActiveNetwork', JSON.stringify(activeNetwork));
localStorage.setItem("ActiveAccount", JSON.stringify(AccountDetails[0]));
}
function copyToClipboard() {
navigator.clipboard.writeText(Phrase)
.then(() => {
https://www.c-sharpcorner.com/ebooks/ 45
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 3000);
})
.catch(err => {
console.error('Error copying to clipboard:', err);
});
}
return (
<div>
<div id="copy-notification-box" className={`copy-notification-box
${isCopied ? 'slide-in opacity' : 'slide-out '}`}>
<p>Passphrase Copied</p>
</div>
<div className="create-backcard">
<div><button type="button" className="create-backButton"
onClick={getBack} ><img src={Back} alt="back" className="create-backButton"
/></button>
</div>
https://www.c-sharpcorner.com/ebooks/ 46
1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-
2.457A13.134 13.134 0 0 1 1.172 8z" />
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M4.5 8a3.5 3.5
0 1 1 7 0 3.5 3.5 0 0 1-7 0" />
</svg>
</div>
<div className="text eye-color" onClick={unHide}>Reveal Secret
Recovery Phrase</div>
<p className="eye-color">Please ensue that no one is watching your
screen</p>
</div>
<div className={`wrapper ${isHidden ? 'display-none' : ''}`}>
{arr.map((item, index) => (
<div key={index} className="phrase">{item}</div>
))}
</div>
<div className="down-buttons">
<button type='button' className="btn btn-primary bttn"
onClick={copyToClipboard} disabled={isHidden}>Copy </button><br />
<button type='button' className="btn btn-primary bttn"
onClick={getNext} disabled={isHidden}>Next</button>
</div>
</div>
</div>
</div>
);
}
Here, I am calling the API endpoint createAccount, that I have created in my server app while
creating the endpoints.
After that storing the account details and showing the passphrase from it on the screen.
If you face any problem while fetching the API response, then you can add the Cors policy to
your server app.
In your server.js app where the endpoints are created, install cors package and include it into
your server project.
After you have installed the package, add the below two lines in your server.js file and then
restart the server app.
//Include cors
const cors = require('cors');
//Use Cors
app.use(cors());
https://www.c-sharpcorner.com/ebooks/ 47
With this there shouldn't be any problem while fetching the data. Please ensure the endpoint
URl while fetching
}
.create-backcard {
width: 550px;
background-color: #ECF0F2;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
box-shadow: 10px 10px 20px #D3DBE7, -0.5rem -0.5rem 20px white;
}
.create-backButton {
margin-top: 15px;
border: none;
background: none;
max-height: 60px;
max-width: 60px;
}
.create-center-align {
width: fit-content;
margin: auto;
}
.create-top-align{
width: 100%;
position: relative;
top: 2%;
}
.your-passphrase {
width: 80%;
margin: 20px auto;
}
h6 {
https://www.c-sharpcorner.com/ebooks/ 48
width: fit-content;
margin: auto;
}
p {
width: 90%;
margin: 20px auto;
text-align: center;
justify-content: center;
}
.padding-change {
padding: 0rem 1rem;
}
.border-radius-change {
border-radius: 10px;
}
.down-buttons {
width: 80%;
margin: 45px auto;
display: flex;
flex-direction: row;
}
.bttn {
margin-left: 6rem;
}
.wrapper {
width: 80%;
margin: auto;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
grid-auto-rows: 50px;
}
.phrase {
background-color: rgba(209, 216, 219, 0.907);
border-radius: 20px;
text-align : center;
color: black;
font-weight: 600;
display: flex !important;
justify-content: center !important;
align-items: center;
}
.logoImage {
width: 10rem;
}
.hide-phrase {
background-color: rgba(25, 23, 23, 0.774);
width: 80%;
https://www.c-sharpcorner.com/ebooks/ 49
height: 14.5rem;
margin: auto;
display: flex;
border-radius: 15px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.display-none {
display: none !important;
}
.eye-color {
color: white ;
cursor: pointer;
}
.copy-notification-box {
background-color: #0d6efd;
color: white;
font-weight: 200%;
width: 20rem;
text-align: center;
float: right;
margin: 20px 20px;
border-radius: 10px;
display: flex;
justify-content: center;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
.copy-notification-box.opacity {
opacity: 1;
}
.copy-notification-box.slide-in {
animation: slideIn 0.5s ease-in-out;
}
.copy-notification-box.slide-out {
animation: slideOut 0.5s ease-in-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
https://www.c-sharpcorner.com/ebooks/ 50
}
}
@keyframes slideOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(100%);
}
}
The page is designed, now add the navigation to navigate to your create wallet page.
Step 5: In your Hom_page.js i.e., the first page, add the URL navigation on the button click
event. In other words, add the functionality in your home page that will take your users to the
Create Wallet page whenever the Create Wallet button is clicked.
openCreate function will be executed whenever the Create Wallet button is clicked. The function
call on the click event should be like:
With this, whenever the Create Wallet button is clicked, the user will be redirected to the
Create_wallet page.
https://www.c-sharpcorner.com/ebooks/ 51
Create Recover Account Action Page
After we have created our Create Wallet page, it’s now time to create the Recover Wallet
Account page.Follow the steps below to create the recover wallet page.
Step 1: Create a new file inside the Component folder and name it Recover_wallet.js.
Step 2: Create the route for the recover page in a similar manner that you created the route for
the Create wallet page in the App.js file.
//Recover_wallet.js
import React, { useState } from "react";
import "./CSS/Create_wallet.css"
import "./CSS/Recover_wallet.css"
import Back from '../../Images/87-875958_back-arrow-in-circle-symbol-removebg-
preview.png'
import logo from '../../Images/yourCryptoLogo-removebg-preview.png';
function Recover_wallet() {
https://www.c-sharpcorner.com/ebooks/ 52
const isAnyFieldEmpty = formData.some(value => value.trim() === '');
if (isAnyFieldEmpty) {
InputBlankError();
}
const combinedString = formData.join(' ');
{ getAccount(combinedString) }
}
function getAccount(passPhrase) {
console.log(passPhrase)
// Calling the recover endpoint created in the server app
const url = `http://localhost:3000/recover/mnemonic/${passPhrase}`
fetch(url, {
method: 'POST'
}).then(response => response.json())
.then(data => {
if (data.message === "Success") {
accountRecovered(data.Mnemonic, data.PrivateKey,
data.Address);
}
else {
accountRecoverError();
}
});
}
setIsRecovered(true);
// Simulate sliding back after 3 seconds
setTimeout(() => {
setIsRecovered(false);
}, 3000);
// Simulate navigating to a new page after 5 seconds
setTimeout(() => {
navigate('/wallet');
}, 4000);
}
https://www.c-sharpcorner.com/ebooks/ 53
}, 4000);
}
const InputBlankError = () => {
setShowInputBlankError(true);
// Simulate sliding back after 3 seconds
setTimeout(() => {
setShowInputBlankError(false);
}, 3000);
}
function SaveToLocalStorage(Phrase, Privatekey, Address) {
const AccountDetails = [{
AccountName: "Account 0",
Mnemonic: Phrase,
PrivateKey: Privatekey,
Address: Address
}];
let activeNetwork = {
NetworkName: "Polygon-Mumbai",
Network: "Testnet",
RPC_URL: "https://rpc-mumbai.maticvigil.com",
Chain_ID: "80001",
Currency: "MATIC"
}
localStorage.setItem('ActiveNetwork', JSON.stringify(activeNetwork));
localStorage.setItem("ActiveAccount", JSON.stringify(AccountDetails[0]));
}
return (
<div>
<div id="recover-confirm-notification-box" className={`recover-
confirm-notification-box ${isRecovered ? 'slide-in opacity' : 'slide-out '}`}>
<p>Account Recovered: Passphrase correct</p>
</div>
<div className="create-backcard">
https://www.c-sharpcorner.com/ebooks/ 54
<div><button type="button" className="create-backButton"
onClick={getBack} ><img src={Back} alt="back" className="create-backButton"
/></button>
</div>
<div className="logo center-align">
<img src={logo} alt='Wallet' className="logoImage policy-tab"
/>
</div>
https://www.c-sharpcorner.com/ebooks/ 55
the user and returns the wallet account with the address and private key which
are later used in the wallet.
Step 4: Design your Recover Wallet page with CSS.
/* Recover_wallet.css */
body {
background-color :#EBEFF2;
max-width: 100%;
max-height: 100%;
overflow: hidden;
}
.phrase-input {
width: 100%;
background-color: none;
border: none;
height: 100%;
background-color: rgba(209, 216, 219, 0.907);
border-radius: 20px;
text-align : center;
color: black;
font-weight: 600;
}
.phrase-input:focus {
background-color: white;
}
.recover-down-buttons {
width: 80%;
margin: 45px auto;
display: flex;
flex-direction: row;
justify-content: space-around;
}
.recover-bttn {
width: 5.5rem;
}
.recover-confirm-notification-box {
background-color: rgb(65, 135, 65);
color: white;
font-weight: 200%;
width: 20rem;
text-align: center;
float: right;
margin: 20px 20px;
border-radius: 10px;
display: flex;
justify-content: center;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
https://www.c-sharpcorner.com/ebooks/ 56
.recover-confirm-notification-box.opacity {
opacity: 1;
}
.recover-confirm-notification-box.slide-in {
animation: slideIn 0.5s ease-in-out;
}
.recover-confirm-notification-box.slide-out {
animation: slideOut 0.5s ease-in-out;
}
.recover-error-notification-box {
background-color: rgb(158, 26, 26);
color: white;
font-weight: 200%;
width: 20rem;
text-align: center;
float:right;
margin: 20px 20px;
border-radius: 10px;
display: flex;
justify-content: center;
position: absolute;
right: 0px;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
.recover-error-notification-box.opacity {
opacity: 1;
}
.recover-error-notification-box.slide-in {
animation: slideIn 0.5s ease-in-out;
}
.recover-error-notification-box.slide-out {
animation: slideOut 0.5s ease-in-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideOut {
from {
opacity: 1;
https://www.c-sharpcorner.com/ebooks/ 57
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(100%);
}
}
Step 5: Modify the route of the Recover Wallet page in the Home page and then view the page.
With this, the recover wallet page is ready to be used. View your page.
Summary
In this chapter, we have started with the Wallet design which would support our server-side
programming and provide an interactive and aesthetic user experience to a new user.
In this chapter, we have involved ourselves with the knowledge of creating a new react project
and designing pages using it. We also learned about React-router-Dom using which we have
navigate through the webpages. With all these we have successfully created designs for our
beginning three pages: Home page, create wallet page and recover wallet page.
Now in the next chapter, we will be moving forward with designing the wallet page where we will
be showing the notifications. So, stay with me, and let’s meet again in the next chapter.
https://www.c-sharpcorner.com/ebooks/ 58
8
Designing the User-
Interface (Part 2)
Overview
https://www.c-sharpcorner.com/ebooks/ 59
Welcome back, Developers! Last time, we made progress by designing the user interface for our
wallet using React.js. We created three essential pages: the Homepage, Create Wallet page,
and Recover Wallet page.
Now, we're moving forward. This chapter focuses on expanding our wallet's capabilities. We'll
work on building a page where users can see notifications in real-time. Also, we'll learn how to
add a new address to the system so we can get notifications effectively.
Let's keep moving and explore the world of real-time blockchain notifications!
Step 1: Create a new JavaScript (.js) file in the Components folder, naming Wallet.js.
Step 2: Modify the App.js file where we are defining the routes for our project. Here import your
Create_account.js file and then define a route to it. After that modify the navigate routes for your
previous pages, create wallet & recover wallet to redirect to the wallet page.
// Wallet.js
import React, { useEffect, useState } from "react";
import './CSS/Wallet.css';
import './CSS/loadingSpinner.css'
import copyLogo from '../../Images/copy.png'
import sendLogo from "../../Images/send.png";
import logo from '../../Images/yourCryptoLogo-removebg-preview.png';
import noData from "../../Images/no_token_nft-removebg-preview.png";
function Wallet() {
https://www.c-sharpcorner.com/ebooks/ 60
useEffect(async () => {
let networkName = await RetrieveNetworkFromLocalStorage()
setActiveNetwork(networkName)
let accountName = RetrieveAccountDetailsFromLocalStorage();
setActiveAccount(accountName);
function RetrieveAccountFromLocalStorage() {
const Account = JSON.parse(localStorage.getItem('ActiveAccount'));
if (Account) {
https://www.c-sharpcorner.com/ebooks/ 61
return (
Account.Address
)
}
}
async function RetrieveNetworkFromLocalStorage() {
const Network = JSON.parse(localStorage.getItem('ActiveNetwork'))
if (Network) {
return (
Network
)
}
}
function RetrieveAccountDetailsFromLocalStorage() {
const Account = JSON.parse(localStorage.getItem('ActiveAccount'))
if (Account) {
return (
Account
)
}
}
function RetrieveRPCFromLocalStorage() {
const Network = JSON.parse(localStorage.getItem('ActiveNetwork'))
if (Network) {
return (
Network.RPC_URL
)
}
}
// Copy Function
function onCopy() {
navigator.clipboard.writeText(RetrieveAccountFromLocalStorage())
.then(() => {
setShowCopiedMessage(true);
setTimeout(() => {
setShowCopiedMessage(false);
}, 3000);
})
.catch(err => {
console.error('Error copying to clipboard:', err);
});
}
https://www.c-sharpcorner.com/ebooks/ 62
setIsNFTActive(false);
setIsTokensActive(true);
};
const handleActivityClick = () => {
setIsActivityActive(true);
setIsNFTActive(false);
setIsTokensActive(false);
};
const handleNFTClick = () => {
setIsActivityActive(false);
setIsNFTActive(true);
setIsTokensActive(false);
};
// HTML Object
return (
<div>
<div id="wallet-notification-box" className={`wallet-notification-box
color-info ${showCopiedMessage ? 'slide-in opacity' : 'slide-out '}`}>
<p>Copied</p>
</div>
<div className="wallet-backcard">
<div className="top-section">
<div className="Network">
<div className="dropdown">
<div className="btn network-button dropdown-toggle
custom-dropdown-toggle d-flex" type="button" id="NetworkdropdownMenu" data-bs-
toggle="dropdown" aria-expanded="false">
<div className="logo-importToken d-flex">
<div className={`importToken-logo static-
logo-color`}>{Array.from(activeNetwork.NetworkName)[0]}</div>
<div>{activeNetwork.NetworkName}</div>
</div>
</div>
</div>
</div>
<div className="dropdown">
<div className="btn account-button dropdown-toggle"
type="button" id="AccountdropdownMenu" data-bs-toggle="dropdown" aria-
expanded="false">
{activeAccount.AccountName}
</div>
</div>
https://www.c-sharpcorner.com/ebooks/ 63
<div className="more-menu" >
<svg xmlns="http://www.w3.org/2000/svg" width="18"
height="18" fill="currentColor" class="bi bi-three-dots-vertical" viewBox="0 0 16
16">
<path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3
0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3
0" />
</svg>
</div>
</div>
<div className="middle-section">
<div className="Address d-flex">
<div className="number">{activeAccount.Address}</div>
<button type="button" className="copy d-flex"
onClick={onCopy}>...<img src={copyLogo} alt="copy" className="copyImage"
/></button>
</div>
<div className="balance">
<div className="value">{Ammout}</div>
<div className="symbol">{activeNetwork.Currency}</div>
</div>
<div className="Transfer d-flex">
<button type="button" className="Send">Send<img
src={sendLogo} alt="Send" className="send-image" /></button>
</div>
<hr />
<div className="navigation d-flex">
<div className={`Tokens ${isTokensActive ? 'navigation-
active' : ''}`} onClick={handleTokensClick} >Tokens</div>
<div className={`Activity ${isNFTActive ? 'navigation-
active' : ''}`} onClick={handleNFTClick}>NFT</div>
<div className={`Activity ${isActivityActive ?
'navigation-active' : ''}`} onClick={handleActivityClick}>Activity</div>
</div>
<div className="navigation-tab-output">
<div className={`ImportedTokens ${!isTokensActive ?
'display-none' : ''}`}>
<div className={`noData`}>
<div className="noData-image"><img src={noData}
alt='NoData' className="" /></div>
<div className="noData-text">No Imported Token's
to show</div>
<div className="noData-link"><a href="#">Lear
more</a></div>
</div>
<div className="importToken">
<div className="bottomLine-On-hover">
https://www.c-sharpcorner.com/ebooks/ 64
<a href="#" className="import"><svg
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-plus" viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0
0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4" />
</svg> Import Token</a>
</div>
</div>
<div className="importToken">
<div className="bottomLine-On-hover">
<a href="/wallet" className="import"> <svg
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1
0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z" />
<path d="M8 4.466V.534a.25.25 0 0 1 .41-
.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466" />
</svg> Refresh List</a>
</div>
</div>
</div>
<div className={`NFTList ${!isNFTActive ? 'display-none'
: ''}`}>
<div className={`noData`}>
<div className="noData-image"><img src={noData}
alt='NoData' className="" /></div>
<div className="noData-text">No NFT's Yet</div>
<div className="noData-link"><a href="#">Lear
more</a></div>
</div>
<div className="importToken">
<a href="#" className="import"><svg
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-plus" viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1
0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4" />
</svg> Import NFT</a>
</div>
<div className="importToken">
<a href="#" className="import"> <svg
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0
4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z" />
<path d="M8 4.466V.534a.25.25 0 0 1 .41-
.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466" />
https://www.c-sharpcorner.com/ebooks/ 65
</svg> Refresh List</a>
</div>
</div>
<div className="footer-section">
<div className="footer-support"><a href="" className="support-
link">@support.yourCrypto</a></div>
</div>
</div>
</div>
);
}
Here, I am calling two endpoints created in the server app. I am fetching the balance of the
address from /balance endpoint and current notifications from the /notifications endpoint.
Step 4: Now let’s add CSS to our wallet. Adding CSS will enhance the wallet’s user interaction
and layout.
/* Wallet.css */
body {
background-color: #EBEFF2;
height: 100%;
}
.wallet-backcard {
width: 1100px;
background-color: #ECF0F2;
position: fixed;
left: 50%;
top: 37%;
transform: translate(-50%,-30%);
box-shadow: 10px 10px 20px #D3DBE7, -0.5rem -0.5rem 20px white;
https://www.c-sharpcorner.com/ebooks/ 66
}
.wallet-logo {
position: fixed;
left: 50%;
top: 10%;
transform: translate(-45%,-95%);
}
.wallet-logo-width {
width: 10rem;
}
.wallet-center-align {
width: fit-content;
margin: auto;
}
.wallet-top-align{
width: 100%;
position: relative;
top: 2%;
}
.Network {
width: fit-content;
margin: auto
}
.top-section{
display: flex;
margin: 0rem 0px 1rem 0rem;
justify-content: space-between;
background-color: white;
box-shadow: 10px 10px 20px #D3DBE7, -0.5rem -0.5rem 20px white;
padding: 0.8rem 0px;
}
.middle-section {
margin: 2rem 0px;
min-height: calc(100vh - 340px);
}
.top-section div {
margin: 0px 1rem;
}
.network-button {
border-radius: 0px;
background: white;
color: black;
border: 2px solid rgb(7, 101, 133);
border-radius: 1.5rem
}
.account-button {
width: 20rem;
border-radius: 0px;
https://www.c-sharpcorner.com/ebooks/ 67
background: white;
color: black;
position: fixed;
left: 35%;
font-size: 1.1rem;
font-weight: 500;
}
.Address {
width: 10rem;
margin: auto;
justify-content: center;
border: 2px solid rgb(7, 101, 133);
border-radius: 10px;
}
.Address .number {
width: 5rem;
overflow: hidden;
padding: 0.2rem;
font-size: 0.9rem;
}
.copy {
height: fit-content;
border: none;
padding: 0.2rem;
}
.copyImage {
width: 1.5rem;
padding: 0.2rem;
background-color: transparent;
}
.balance {
width: 50%;
margin: 0.5rem auto;
display: flex;
justify-content: center;
}
.balance .value {
font-size: 3.5rem;
align-self: end;
}
.balance .symbol {
align-self: end;
font-size: 1rem;
color: rgb(7, 101, 133);
}
.importToken{
margin-top: 0.5rem;
https://www.c-sharpcorner.com/ebooks/ 68
display: block;
width: fit-content;
}
hr {
width: 60%;
margin: auto !important;
color: rgb(3, 53, 69);
height: 5px !important;
border-radius: 150px;
}
.importToken-logo{
padding: 4px 12px;
width: fit-content !important;
margin: 0px 5px 0px auto !important;
border-radius: 100px;
font-style: inherit;
font-size: 1.1rem;
font-weight: 500;
}
.Activity-Info {
justify-content: space-between;
padding: 5px 15px;
border-bottom: 2px solid rgb(7, 101, 133);
margin: 4px 0px;
cursor: pointer;
}
.ActivityList {
overflow-y:auto;
}
.display-none {
display: none;
}
.next {
width: 30%;
margin-left: 8px;
}
.next-logo-div {
align-self: center;
}
.Send {
width: 7rem;
margin: 0px auto 15px;
border : none;
background-color: rgb(59, 147, 201);
color: white;
padding: 10px 15px;
border-radius: 50px;
font-weight: 500;
https://www.c-sharpcorner.com/ebooks/ 69
}
.send-image {
width: 1.5rem;
margin: 0px 0px 0px 5px;
}
.navigation {
width: 40%;
margin: 10px auto;
justify-content: space-between;
}
.Tokens, .Activity {
width: 35%;
text-align: center;
font-size: 110%;
font-weight: 400;
cursor: pointer;
}
.Tokens:hover, .Activity:hover {
color: rgb(34, 112, 179);
}
.navigation-active{
border-bottom: 2px solid rgb(34, 112, 179);
}
.custom-dropdown-toggle {
display: flex;
align-items: center;
}
.static-logo-color {
background-color: lightblue;
}
.coloumn {
width: 40%;
margin: auto;
}
.text-align-center {
text-align: center;
}
.form-control {
padding: 8px;
margin-top: 10px ;
}
.navigation-tab-output {
width: 60%;
height: 20rem;
margin: 1rem auto;
}
.footer-section {
margin: 2rem;
https://www.c-sharpcorner.com/ebooks/ 70
display: flex;
justify-content: center;
align-items: center;
}
.support-link{
text-decoration: none;
font-family: Verdana, Geneva, Tahoma, sans-serif;
cursor: pointer;
font-weight: 500;
}
.support-link:hover {
border-bottom: 2px solid #6489f7b8;
}
.noData {
width: fit-content;
margin: auto;
}
.noData-image {
width: fit-content;
margin: auto;
}
.noData-text {
color: #aeb1b4;
font-weight: 500;
font-size: 1.1rem;
font-family: cursive;
}
.noData-link {
margin: auto;
font-family: cursive;
width: fit-content;
}
.noData-link a {
text-decoration: none;
}
/* Notification box */
.wallet-notification-box {
background-color: rgb(65, 135, 65);
color: white;
font-weight: 200%;
width: 20rem;
text-align: center;
float: right;
margin: 20px 20px;
border-radius: 10px;
display: flex;
justify-content: center;
opacity: 0;
https://www.c-sharpcorner.com/ebooks/ 71
transition: opacity 0.5s ease-in-out;
position: absolute;
right: 0px;
opacity: 0;
}
.color-info{
background-color: rgb(20, 154, 198);
}
.wallet-notification-box.opacity {
opacity: 1;
}
.wallet-notification-box.slide-in {
animation: slideIn 0.5s ease-in-out;
}
.wallet-notification-box.slide-out {
animation: slideOut 0.5s ease-in-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(100%);
}
}
https://www.c-sharpcorner.com/ebooks/ 72
But wait, even though the wallet is created, and we are good to go, but still, won’t be getting any
notification from the Alchemy webhook to our personal server. You ask the reason?
Well, it’s because the new wallet’s address won’t be added to the webhook. If your new wallet’s
address is added, then it is good enough you will receive the updates but if not, you need to add
the wallet address to the webhook. So how do I add a new wallet address? Let’s look.
https://www.c-sharpcorner.com/ebooks/ 73
Step 5: Click on the Update Webhook button to update the webhook addresses.
if (results[0].PresentOrNot == 1) {
res.setHeader('Content-Type', 'application/json');
res.status(200).json({ message: "Address already added for
monitoring" });
}
else if (results[0].PresentOrNot == 0) {
let finalResult = await UpdateWebhookAddress(address)
if(finalResult.statusCode == true){
res.setHeader('Content-Type', 'application/json');
https://www.c-sharpcorner.com/ebooks/ 74
}
}
})
})
const requestData = {
webhook_id: process.env.WebhookId,
addresses_to_add: address,
addresses_to_remove: []
};
Here, I am defining an endpoint which accepts the address which needs to be chacked and
updated in the webhook.
The address needs to be provided in the form of array of strings, i.e., [“user address”].
https://www.c-sharpcorner.com/ebooks/ 75
There is a table created in the database where we are keeping track of all the addresses added
to the webhook. The user address is first checked in the database to confirm it is already added
to the webhook or not. If not, then it is added to the webhook using the alchemy API.
Restart your server without closing your ngrok port to see the changes.
After adding the server-side code. Call the endpoint in your React Wallet file “Wallet.js”
Summary
In this chapter, we delved into enhancing the functionality of our wallet application by
implementing real-time notifications. We began by creating the wallet page, where users can
view their account details, balances, and transaction activity. The page was designed using
React.js, with various components structured to provide a seamless user experience.
Key steps covered in this chapter include:
• Creating the Wallet Page: We developed the wallet page by defining its layout and
integrating essential functionalities such as fetching account balances and transaction
notifications from the server.
• Implementing Real-Time Notifications: Through API requests to the server, we enabled
the retrieval of real-time notifications for user transactions, enhancing the user's
awareness of account activity.
• Styling the Wallet Interface: CSS styling was applied to the wallet page to improve its
visual appeal and user interaction, ensuring a polished and professional look.
Additionally, we addressed the crucial aspect of adding new wallet addresses to the Alchemy
webhook, allowing users to receive notifications for transactions involving their accounts. Two
methods were discussed: manual addition through the Alchemy dashboard UI and automated
addition via a custom API endpoint.
With the completion of this chapter, the book concludes its exploration of wallet development,
providing developers with the tools and knowledge to enrich their applications with real-time
notification capabilities. By following the steps outlined in this guide, developers can create
robust and user-friendly wallet solutions tailored to their specific needs and requirement
https://www.c-sharpcorner.com/ebooks/ 76
OUR MISSION
Free Education is Our Basic Need! Our mission is to empower millions of developers worldwide by
providing the latest unbiased news, advice, and tools for learning, sharing, and career growth. We’re
passionate about nurturing the next young generation and help them not only to become great
programmers, but also exceptional human beings.
ABOUT US
CSharp Inc, headquartered in Philadelphia, PA, is an online global community of software
developers. C# Corner served 29.4 million visitors in year 2022. We publish the latest news and articles
on cutting-edge software development topics. Developers share their knowledge and connect via
content, forums, and chapters. Thousands of members benefit from our monthly events, webinars,
and conferences. All conferences are managed under Global Tech Conferences, a CSharp
Inc sister company. We also provide tools for career growth such as career advice, resume writing,
training, certifications, books and white-papers, and videos. We also connect developers with their poten-
tial employers via our Job board. Visit C# Corner
MORE BOOKS