How to Create a Signature Generator DApp
We will create three React components and build all of the necessary functionality to connect MetaMask to our front-end DApp, sign messages, and verify message signatures
Clone Starter Files
- First, clone the signature-generator-app repository to get your starter files on your local machine. This repository provides all of the necessary styling and UI. (If you need assistance cloning a repository, checkout GitHub’s documentation.)
- Open the
starter-files
in your preferred IDE and navigate to the/src/components
folder. Here, you will find all of the React components that make up our app, as well as astyles
folder with all of our styling sheets. Within the React components, theWalletButton.jsx
,SignMessage.jsx
, andVerifySignature.jsx
have empty functions that we will build out in the reset of this tutorial. The rest of the components are fully functional so we won’t worry about editing them in this tutorial.
Next, let’s install all of the necessary modules and deploy a local development server before begin building our components.
In your terminal:
- Navigate to your local repository with `cd starter files.
- Run
npm install
to install your dependencies. - Run
npm start
to deploy a local development server athttp://localhost:3000/
.
At your local server, you should see the front end of our Signature Generator app. It should contain:
- A Sign Message component.
- A Verify Signature component.
- A Generated Signatures component.
- A Connect Wallet button.
The Generated Signatures component is complete and displays a list of all the signatures we generate with a timestamp, address, and message field. At this point, we haven’t created any signatures so we’ve haven’t included any example signatures.
You will notice that the other buttons don’t work yet. Let’s move on to the next steps to begin adding some functionality!
Component 1: Connect Wallet Button
Navigate to the /src/components
folder and open WalletButton.jsx
. Inside, you should see five different functions with the commented text Code goes here…
in the function body.
We are going to add functionality to each of these empty functions to get our Connect Wallet button working.
You will notice we are importing useState
and useEffect
. These are the React hooks we will use to manage the state of our button. For more information about React hooks and components, check out React’s documentation: Introducing Hooks and Components and Props.
Below is what your WalletButton.jsx
component should look like:
connectWallet
The connectWallet
function allows us to request a list of account addresses from MetaMask.
Copy the following into your connectWallet
function body:
The above code directs the connectWallet
function to check whether MetaMask is installed and if so, returns the user’s account address. If MetaMask is not installed, the function will throw an error and let the user know they need to install MetaMask.
For more detailed descriptions of smaller pieces of code, check out the commented descriptions throughout the examples.
getConnectedWallet
If a user is already connected we still need a way to retrieve the currently connected addresses. This is what our getConnectedWallet
function will do.
Copy the following into the getConnectedWallet
function’s body:
Great! The above code retrieves all of the MetaMask account’s addresses.
walletConnectHandler
The walletConnectHandler
function makes the Connect Wallet button respond to user clicks by calling the connectWallet
function. Additionally, it sets the walletAddress
state so our button renders the correct account address.
To achieve this, add the following code to our walletConnectHandler
function:
addWalletListener
Awesome, our connect wallet button is almost functional! However, we still need a way to detect if the user decides to switch to a different account. If a user does switch accounts, we will need to update our walletAddress
state.
To achieve this, let’s build our addWalletListener
function to update our button if the user changes accounts:
load
Finally, we will create the load
function to handle all of the code we want to run after React has updated the DOM. With this, we can run the load function in the useEffect
hook and perform all of our needed side effects.
Copy the following into the load
function’s body:
Below our load function, we add the useEffect
hook and call load
inside, like this:
Nice going, you just created a functioning Connect Wallet button that connects to a MetaMask wallet! We can use this button to connect MetaMask to our signature generator. If you’d like, you can try out the button at your local development server.
Component 2: Signing Messages
For our signer component, we want to make a user’s message and prompt them with a signature request from MetaMask. We will also add a timestamp to the signature to display in our generated signatures component. Finally, let’s have this all happen when the user clicks on the sign message button.
To start, navigate to the SignMessage.jsx
starter file. It should look like this, with commented-out functions:
messageToSign
The messageToSign
function takes a message from our user text input and our error state. Our objective is to make this function pass along the user’s message to MetaMask and await their signature. We also want to create our own timestamp and return an object that includes that timestamp, as well as the message, signature hash, and signer address. This object can later be passed to an array in our generated signatures component to be displayed dynamically there.
Let’s walk through the above step-by-step in the function below. Copy the following into your messageToSign
function’s body:
Now our function does everything we want it to do, except create signature timestamp. We can add a timestamp by creating a date object, such as:
Our function now passes along the user’s message to MetaMask and awaits their signature. It also creates a timestamp and returns all of the values we need for our generated signatures component.
signMessageHandler
Now, we need to add some responsiveness when the user clicks the Sign Message button. To do this, we should add some functionality to the signMessageHandler
function:
Now, add an onSubmit
pointer to the signMessageHandler
function inside the form element:
Awesome! We can now sign messages and see them in our generated signatures component! Try it out on your local development server.
Component 3: Verifying Message Signatures
In our final step, we need to input an address, message, and signature hash and check whether they are valid using the Ethers.js recoverAddress
function.
Navigate to the VerifySignature.jsx
component, which should look like this (again, functions commented out):
signatureToVerify
First, let’s create our signatureToVerify
function which takes an address, message, and signature and returns either a valid or invalid response.
Add the following to your signatureToVerify
function body:
The function now returns true if the signer address matches the entered address or false if they don’t match.
verifySignatureHandler
Now, we have to ensure our signatureToVerify
function retrieves data from the form submission when the user clicks the Verify Signature button.
The above can be accomplished in the verifySignatureHandler
function. Add the following code to the function’s body:
Lastly, add an onSubmit
pointer to our verifySignatureHandler
function inside our form element:
Congratulations! You have just completed this tutorial and created a fully functional signature generator and verifier! Try verifying some signatures on your own and, as an experiment, change a single letter of your message to see that the signature becomes invalid.
What You Learned:
In this tutorial, you learned (and completed) the following things:
- How to sign messages and verify signatures using Ethers.js and Alchemy’s Web3.js
- How to build a frontend React app and connect it to MetaMask
Feel free to reach out to us in the Alchemy Discord for help or @Alchemy on Twitter and let us know you just finished building your signature generator!