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 = await wallet.getAddress()
const chainId = await 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 sequence.utils.isValidMessageSignature(
walletAddress,
message,
signature,
provider,
chainId // optional, as it will use the chain id from the provider
)
console.log(isValid)
As a Smart Contract-based account, Sequence signatures must be validated with the standard EIP1271 method, see https://eips.ethereum.org/EIPS/eip-1271.
The sequence.utils.isValidMessageSignature
utility function is an easy way to validate any kind of message signature from any wallet.
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", etcwalletAddress
(string) -- the wallet addressmessage
(string) -- the message in utf8 text encodingsignature
(string) -- the signature in hex encoding
IsValidMessageSignature
example usage:
- curl
- Javascript
- Go
- Other
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" }'
// Works in both a Webapp (browser) or Node.js:
import { sequence } from '0xsequence'
const chainId = 'polygon'
const walletAddress = '0x2fa0b551fdFa31a4471c1C52206fdb448ad997d1'
const message = 'Hi, please sign this message'
const signature = '0x000501032a44625bec3b842df681db00a92a74dda5e42bcf0203596af90cecdbf9a768886e771178fd5561dd27ab005d0001000183d971056b1eca1bcc7289b9a6926677c5b07db4197925346367f61f2d09c732760719a91722acee0b24826f412cb69bd2125e48f231705a5be33d1f5523f9291c020101c50adeadb7fe15bee45dcb820610cdedcd314eb0030002f19915df00d669708608502d3011a09948b32674d6e443202a2ba884a4dcd26c2624ff33a8ee9836cc3ca2fbb8d3aa43382047b73d21646cb66cc2916076c1331c02'
const api = new sequence.api.SequenceAPIClient('https://api.sequence.app')
const { isValid } = await api.isValidMessageSignature({
chainId, walletAddress, message, signature
})
console.log(isValid) // true
import (
"context"
"fmt"
"log"
"net/http"
"github.com/0xsequence/go-sequence/api"
)
func ValidateMessageSignature() {
seqAPI := api.NewAPIClient("https://api.sequence.app", http.DefaultClient)
chainID := "polygon"
walletAddress := "0x2fa0b551fdFa31a4471c1C52206fdb448ad997d1"
// NOTE: you can also pass the message in EIP191 format as well. If you do not pass
// the EIP191 prefix, it will automatically be added at the time of validation.
// message := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(message), message)
message := "Hi, please sign this message"
signature := "0x000501032a44625bec3b842df681db00a92a74dda5e42bcf0203596af90cecdbf9a768886e771178fd5561dd27ab005d0001000183d971056b1eca1bcc7289b9a6926677c5b07db4197925346367f61f2d09c732760719a91722acee0b24826f412cb69bd2125e48f231705a5be33d1f5523f9291c020101c50adeadb7fe15bee45dcb820610cdedcd314eb0030002f19915df00d669708608502d3011a09948b32674d6e443202a2ba884a4dcd26c2624ff33a8ee9836cc3ca2fbb8d3aa43382047b73d21646cb66cc2916076c1331c02"
isValid, err := seqAPI.IsValidMessageSignature(context.Background(), chainID, walletAddress, message, signature)
if err != nil {
log.Fatal(err)
}
fmt.Println("isValid?", isValid)
}
Please contact our team for assistance with integrations to another target.
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", etcwalletAddress
(string) -- the wallet addressethAuthProofString
(string) -- the ETHAuth encoded signature
IsValidETHAuthProof
example usage:
- curl
- Javascript
- Go
- Other
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"}'
// Works in both a Webapp (browser) or Node.js:
import { sequence } from '0xsequence'
const chainId = 'polygon'
const walletAddress = '0x2fa0b551fdFa31a4471c1C52206fdb448ad997d1'
const ethAuthProofString = 'eth.0x2fa0b551fdfa31a4471c1c52206fdb448ad997d1.eyJhcHAiOiJEZW1vIERhcHAiLCJpYXQiOjAsImV4cCI6MTY2MDIzMTAyOCwidiI6IjEiLCJvZ24iOiJodHRwOi8vbG9jYWxob3N0OjQwMDAifQ.0x000501032a44625bec3b842df681db00a92a74dda5e42bcf0203596af90cecdbf9a768886e771178fd5561dd27ab005d00010001f7dad5ade840bb961cbab889d731bbc080bb4c36fc090435e82fe78e3c152b671505ad544adb562cc25a5933cd06c9108e239a52a82ba797c3d3522645c69cd81b020101c50adeadb7fe15bee45dcb820610cdedcd314eb003000274164fb33c93b4384582c54c30d9a1e2ef219063d03084005edc1da853af2f1f2e67275dbb6ef945d04600b6dd83cfd997cc9ae4173ea61b0c5cc0808fb196681b02'
const api = new sequence.api.SequenceAPIClient('https://api.sequence.app')
const { isValid } = await api.isValidETHAuthProof({
chainId: chainId, walletAddress: walletAddress, ethAuthProofString: ethAuthProofString
})
console.log(isValid) // true
import (
"context"
"fmt"
"log"
"net/http"
"github.com/0xsequence/go-sequence/api"
)
func ValidateETHAuth() {
seqAPI := api.NewAPIClient("https://api.sequence.app", http.DefaultClient)
chainID := "polygon"
walletAddress := "0x2fa0b551fdFa31a4471c1C52206fdb448ad997d1"
ethAuthProofString := "eth.0x2fa0b551fdfa31a4471c1c52206fdb448ad997d1.eyJhcHAiOiJEZW1vIERhcHAiLCJpYXQiOjAsImV4cCI6MTY2MDIzMTAyOCwidiI6IjEiLCJvZ24iOiJodHRwOi8vbG9jYWxob3N0OjQwMDAifQ.0x000501032a44625bec3b842df681db00a92a74dda5e42bcf0203596af90cecdbf9a768886e771178fd5561dd27ab005d00010001f7dad5ade840bb961cbab889d731bbc080bb4c36fc090435e82fe78e3c152b671505ad544adb562cc25a5933cd06c9108e239a52a82ba797c3d3522645c69cd81b020101c50adeadb7fe15bee45dcb820610cdedcd314eb003000274164fb33c93b4384582c54c30d9a1e2ef219063d03084005edc1da853af2f1f2e67275dbb6ef945d04600b6dd83cfd997cc9ae4173ea61b0c5cc0808fb196681b02"
isValid, err := seqAPI.IsValidETHAuthProof(context.Background(), chainID, walletAddress, ethAuthProofString)
if err != nil {
log.Fatal(err)
}
fmt.Println("isValid?", isValid)
}
How does it work?
Notes on Signature Validation with EIP1271
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.
For Javascript/Typescript signature verification, you can use 0xsequence
utility functions like so:
import { 0xsequence } from '0xsequence'
const isValid = await sequence.utils.isValidSignature(
walletAddress,
digest,
signature,
provider,
chainId
)
console.log(isValid) // returns true/false
Additionally you can also use sequence.utils.isValidMessageSignature
or sequence.utils.isValidTypedDataSignature
which are just syntactic sugar for sequence.utils.isValidSignature
.
As well, for convenience the signature validation fucntions 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.