Build
Skip to content

Signing & Verifying Messages

Signing Messages

Sequence wallets are able to sign arbitrary messages.

To request a user's signature of a simple message:
const signer = wallet.getSigner();
const message = "Hello World!";
 
const signature = await signer.signMessage(message);
console.log(signature);
To request a user's signature of a typed-data (EIP712) message:
const typedData: sequence.utils.TypedData = {
  domain: {
    name: "Ether Mail",
    version: "1",
    chainId: await wallet.getChainId(),
    verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
  },
  types: {
    Person: [
      { name: "name", type: "string" },
      { name: "wallet", type: "address" },
    ],
  },
  message: {
    name: "Bob",
    wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
  },
};
 
const signer = wallet.getSigner();
 
const signature = await signer.signTypedData(
  typedData.domain,
  typedData.types,
  typedData.message
);
console.log(signature);

Verifying Message Signatures

Once you have a signature, you'll often want to verify the validity of the data either from your app or from your server. The Sequence SDKs make this easy to do from either your frontend or backend.

Given a message and signature, you can check if a particular wallet signed that message:

// Here we fetch the different parameters, but in practice you may have these values
// encoded and passed separately.
const wallet = sequence.getWallet();
const provider = wallet.getProvider();
const walletAddress = wallet.getAddress();
const chainId = wallet.getChainId();
 
// The sequence utils `isValidMessageSignature` method can validate signatures
// from any kind of wallet (ie. EOA or Smart Wallet) which includes Metamask, Coinbase,
// and Sequence.
const isValid = await wallet.utils.isValidMessageSignature(
  walletAddress,
  message,
  signature,
  chainId
);
 
console.log(isValid);

Verifying Message Signatures (via Sequence API)

The Sequence API also offers the convenience to verify any wallet message signature by making a simple remote API call.

The Sequence API (https://api.sequence.app) supports the following RPC methods:

  • /rpc/API/IsValidMessageSignature -- verifying a simple text message signature
  • /rpc/API/IsValidTypedDataSignature -- verifying a EIP712 typed data object
  • /rpc/API/IsValidSignature -- verifying an arbitrary message digest
  • /rpc/API/IsValidETHAuthProof -- verifying an ETHAuth proof

The most common methods are IsValidMessageSignature and IsValidETHAuthProof.

Verifying message signature from any kind of wallet (ie. Metamask or Sequence)

Sequence API IsValidMessageSignature Method:

  • Request: POST https://api.sequence.app/rpc/API/IsValidMessageSignature
  • Content-Type: application/json
  • Body (in JSON):
    • chainId (string) -- the chain id of the signature, ie. "1" or "mainnet", or "137" or "polygon", etc
    • walletAddress (string) -- the wallet address
    • message (string) -- the message in utf8 text encoding
    • signature (string) -- the signature in hex encoding

IsValidMessageSignature example usage:
cURL
curl -X POST -H "Content-Type: application/json" https://api.sequence.app/rpc/API/IsValidMessageSignature -d '{ "chainId": "polygon", "walletAddress": "0x2fa0b551fdFa31a4471c1C52206fdb448ad997d1", "message": "Hi, please sign this message", "signature": "0x000501032a44625bec3b842df681db00a92a74dda5e42bcf0203596af90cecdbf9a768886e771178fd5561dd27ab005d0001000183d971056b1eca1bcc7289b9a6926677c5b07db4197925346367f61f2d09c732760719a91722acee0b24826f412cb69bd2125e48f231705a5be33d1f5523f9291c020101c50adeadb7fe15bee45dcb820610cdedcd314eb0030002f19915df00d669708608502d3011a09948b32674d6e443202a2ba884a4dcd26c2624ff33a8ee9836cc3ca2fbb8d3aa43382047b73d21646cb66cc2916076c1331c02" }'

Verifying ETHAuth proof upon connecting a Sequence Wallet

Sequence API IsValidETHAuthProof Method:

  • Request: POST https://api.sequence.app/rpc/API/IsValidETHAuthProof
  • Content-Type: application/json
  • Body (in JSON):
    • chainId (string) -- the chain id of the signature, ie. "1" or "mainnet", or "137" or "polygon", etc
    • walletAddress (string) -- the wallet address
    • ethAuthProofString (string) -- the ETHAuth encoded signature

IsValidETHAuthProof example usage:
cURL
curl -X POST -H "Content-Type: application/json" https://api.sequence.app/rpc/API/IsValidETHAuthProof -d '{"chainId":"polygon", "walletAddress":"0x2fa0b551fdFa31a4471c1C52206fdb448ad997d1","ethAuthProofString": "eth.0x2fa0b551fdfa31a4471c1c52206fdb448ad997d1.eyJhcHAiOiJEZW1vIERhcHAiLCJpYXQiOjAsImV4cCI6MTY2MDIzMTAyOCwidiI6IjEiLCJvZ24iOiJodHRwOi8vbG9jYWxob3N0OjQwMDAifQ.0x000501032a44625bec3b842df681db00a92a74dda5e42bcf0203596af90cecdbf9a768886e771178fd5561dd27ab005d00010001f7dad5ade840bb961cbab889d731bbc080bb4c36fc090435e82fe78e3c152b671505ad544adb562cc25a5933cd06c9108e239a52a82ba797c3d3522645c69cd81b020101c50adeadb7fe15bee45dcb820610cdedcd314eb003000274164fb33c93b4384582c54c30d9a1e2ef219063d03084005edc1da853af2f1f2e67275dbb6ef945d04600b6dd83cfd997cc9ae4173ea61b0c5cc0808fb196681b02"}' 

How does it work?

Notes on Signature Validation with EIP1271 + EIP6492

Smart Wallets like Sequence rely on the EIP1271 standard for signature validation.

The EIP1271 is a single function on a contract defined as:

function isValidSignature(
  bytes32 _hash,
  bytes memory _signature
) public view returns (bytes4 magicValue)

The first _hash argument accepts the hash of the message digest, and the second argument _signature is the signed payload returned by the wallet upon signing.

Additionally, Smart Wallets don't always deploy a contract onchain every time a new wallet is created. Instead, they compute the wallet address deterministically, and the wallet is only deployed when a transaction needs to be made.

In this case, we can't use the EIP1271 function directly, because the wallet contract doesn't exist yet. Instead, we use the EIP-6492 standard, which defined a method for bootstrapping the wallet contract and calling the EIP1271 function, in a single operation.

For Javascript/Typescript signature verification, you can use 0xsequence utility functions like so:

import { 0xsequence } from '0xsequence'
 
const wallet = sequence.getWallet()
 
const isValid = await wallet.utils.isValidSignature(
  walletAddress,
  digest,
  signature,
  chainId
)
 
console.log(isValid) // returns true/false

Additionally you can also use wallet.utils.isValidMessageSignature or wallet.utils.isValidTypedDataSignature which are just syntactic sugar for wallet.utils.isValidSignature.

As well, for convenience the signature validation functions above support verifying EOA or Smart Wallet signatures. This allows you to use a single code path in your Dapp to verify any kind of signature and support multiple wallets at the same time, like Metamask, Coinbase, Sequence, WalletConnect, Argent, Rainbow, etc. -- all Ethereum compatible wallets, EOA or Smart Wallets, will just work.

Legacy non-EIP6492 Signing

By default, all the signing methods will generate EIP-6492 encoded signatures. This avoids the need to deploy the wallet onchain before being able to validate the signature, and is the recommended way to sign messages.

However, if you need to generate legacy non-EIP6492 signatures, you can do so by setting the last argument of the signing methods to false:

const signer = wallet.getSigner();
const message = "Hello World!";
 
const signature = await signer.signMessage(message, { eip6492: false });
console.log(signature);

These legacy signatures can be validated using the wallet.utils.isValidSignature method, but they can also be validated using the isValidSignature method defined on the wallet contract, as specified by the EIP1271 standard.