Going Down the EIP-712 Rabbit Hole
Miguel Piedrafita
0xE340…DCb39DI spent way too much time figuring out how to generate and validate typed data signatures so you don't have to.
For a project I’m working on, I need users to sign some data with their wallet. Normally, I’d just use personal signatures, but in this case, the data has a specific format, and I wanted to get the wallet signature prompt looking a little bit nicer than usual.
The World of Ethereum Signatures
When signing some data with an Ethereum wallet, there are multiple signature types you can choose from. You can use the “regular” one, which will sign any string (this is what’s used to sign transactions); you can use personal signatures, which add a prefix to the signed message; or you can use typed data signatures, which allow you to define a format for the message the user will sign (and display it nicely on the signature prompt).
Let’s Type some Data
For this project, I decided to go with typed data signatures (or EIP-712 signatures). To start, you need to define your types
(the structure the message will be using) and your domain
(some information about your app).
// Let's assume we're signing an email message
const message = {
from: {
name: 'Miguel Piedrafita',
wallet: '0xE340b00B6B622C136fFA5CFf130eC8edCdDCb39D'
},
to: {
name: 'Alex Masmej',
wallet: '0xD3e9D60e4E4De615124D5239219F32946d10151D'
},
contents: 'We need more NFTs.'
};
// Our domain will include details about our app
const domain = {
name: 'Ether Mail',
version: '1',
};
// Here we define the different types our message uses
const types = {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' }
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' }
]
};
Once we have our domain
and types
, we can use the ethers.js
library to get our signature.
import { ethers } from 'ethers'
const web3 = new ethers.providers.Web3Provider(provider)
const signature = await web3.getSigner._signTypedData(
domain, types, message,
)
The Verificathon
Now we just need a way of verifying the signature in our server. For this, we’ll need the signature we just generated and the wallet address of the signer, as well as our domain
and types
from the last step.
import { verifyTypedData } from 'ethers/lib/utils'
export const verifySignature = (signature, message, address): boolean => {
return verifyTypedData(
domain, types, message, signature,
).toLowerCase() === address.toLowerCase()
}
In Summary
This took me quite a while to figure out (+2h 😅), so I thought I’d write it down for the next person that decides to go down this rabbit hole. Hopefully, it’ll help!