Miguel Piedrafita

ENS

miguel.build

Going Down the EIP-712 Rabbit Hole

Miguel Piedrafita

0xE340…DCb39D

I 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!