How to sign data and verify the address that generated the signature using Wagmi

In this tutorial, we are going to learn how to sign data using the wallet connected to your app and how to verify the address that generated the signature using Wagmi hooks.

In this tutorial, we are going to learn how to sign data using the wallet connected to your app and how to verify the address that generated the signature using Wagmi hooks.

Here is the code to sign a message and get the address that signed it:

import { useEffect, useState } from 'react';
import { useSignMessage } from 'wagmi';
import { utils } from 'ethers';

const messageToSign = '1234567890';

export const SignAndVerify = () => {
  const [signerAddress, setSignerAddress] = useState();

  const { data: signature, isLoading, isError, signMessage } = useSignMessage({
    message: messageToSign,
  });

  useEffect(() => {
    if (signature) {
      // verify the signature and get the address that generated the signature for the message passed
      setSignerAddress(utils.verifyMessage(messageToSign, signature));
    }
  }, [signature]);

  if (isLoading) return <p>Please confirm on your wallet...</p>;
  if (isError) return <p>Could not sign the message</p>;

  return (
    <div>
      {signature && <p>The signature that was generated: {signature}</p>}
      {signerAddress && <p>This address signed the message: {signerAddress}</p>}
      <button disabled={!signMessage} onClick={() => signMessage()}>
          Sign message
      </button>
    </div>
  );
};

Now let's take a deeper look at what this code does.

How to sign a message using Wagmi

To sign a message using Wagmi, we use the useSignMessage hook.

Like the other Wagmi hooks, it takes in parameters an object that contains config values for the hook. In that object, we pass the message that we want to sign in the message property.

If you don't pass anything in the message property, it won't do anything.

For example, it allows you to have the message to sign in the state of your component and when a value is set to that state variable, it generates a signature. When the message changes, it re-generates a signature.

It also takes other properties in the config object, check out the documentation to see everything you can pass.

That hook returns an object containing the following properties:

  • data: the signature that was generated (an hexadecimal string)
  • signMessage: the function to call to sign the message you passed in the parameters. It will open a screen in the user's wallet to ask them to sign the message. It will set isLoading to true while waiting for the user's confirmation
  • isLoading: true while waiting for the user to sign the message on their wallet and false before calling signMessage and after the user signed the message on their wallet.
  • isSuccess: true if the user signed the data and false if there was an error or if signMessage hasn't been called yet.
  • isError: true if signing the message failed (for example if the user rejected signing the data on their wallet) and false if it succeeded or if signMessage hasn't been called yet.
  • error: if signing the message failed, this property contains the error that was thrown when trying to sign the data. It will be null if there was no error.

An important thing to note here is that you need a Signer to use that hook. In other words, a wallet needs to be connected to your application to be able to use that hook.

Otherwise, the signMessage function will be undefined. That's why in the example above, I disable the button if signMessage is not defined, it's to avoid calling that function when it's undefined.

Here is an example of how to sign data:

import { useSignMessage } from 'wagmi';

const messageToSign = '1234567890';

export const SignMessage = () => {
  const { data, isLoading, isError, signMessage } = useSignMessage({
    message: messageToSign,
  });

  if (isLoading) return <p>Please confirm on your wallet...</p>;
  if (isError) return <p>Could not sign the message</p>;

  return (
    <div>
      {data && <p>The signature that was generated: {data}</p>}
      <button disabled={!signMessage} onClick={() => signMessage()}>
          Sign message
      </button>
    </div>
  );
};

How to verify a signature and get the address that generated the signature

To get the address that generated the signature, we have to use Ethers JS directly. Wagmi doesn't provide a hook for that.

For that, we can use the verifyMessage that is in the utils of Ethers JS.

That function takes in 2 parameters:

  1. the message that was signed
  2. the signature to verify

And it returns the address that generated the signature you passed in the parameters according to the data you passed.

If you pass the same signature but different data, you won't get the same result so make sure you pass the exact same data that was used to generate the signature.

import { utils } from 'ethers'

export const VerifySignature = ({ data, signature }) => {
  return (
    <p>
      The signature {signature},
      generated from  message "{data}",
      was signed with {utils.verifyMessage(data, signature)}
    </p>
  )
}

If you want, you can also verify signatures in Solidity smart contracts. If you want to learn how to do that, check out this tutorial:

How to verify a signature in a smart contract in Solidity
We are going to learn how to verify signatures in a Solidity smart contract and learn how to get the address that generated the signature.

And if you want to learn how to make a complete authentication system on Ethereum using signatures like this, check out this tutorial:

How to implement a sign in with Ethereum wallet flow Web3 JS
In this guide, we are going to learn how to implement an authentication system that allows users to log in using an Ethereum wallet with Web3 JS.

And that's it 🎉

Thank you for reading this article