patternjavascriptModerate
EIP-712: typed data signing for structured off-chain messages
Viewed 0 times
ethers.js v6.x, EIP-712
EIP-712typed datasignTypedDatadomain separatorpermitstructured signing
Problem
EIP-191 personal_sign shows the user an unreadable hex string. EIP-712 allows wallets to display structured, human-readable data for the user to sign.
Solution
Define a domain separator and type hash, then use signer._signTypedData() (ethers v5) or signer.signTypedData() (ethers v6).
const domain = { name: 'MyApp', version: '1', chainId: 1, verifyingContract: address };
const types = { Permit: [{ name: 'owner', type: 'address' }, { name: 'value', type: 'uint256' }] };
const signature = await signer.signTypedData(domain, types, value);Why
EIP-712 is used by ERC-2612 Permit (gasless token approvals), many DEX order systems, and any protocol that needs signed structured data.
Gotchas
- The chainId in the domain must match the actual chain or wallets will reject the signature
- EIP-712 type strings are order-sensitive — the order of fields in the type definition must match the struct
- In ethers v5 it was _signTypedData (with underscore); in v6 it is signTypedData (without underscore)
Code Snippets
EIP-712 typed data signing with ethers v6
import { ethers } from 'ethers';
async function signPermit(signer, tokenAddress, spender, value, deadline, nonce) {
const domain = {
name: 'MyToken',
version: '1',
chainId: (await signer.provider.getNetwork()).chainId,
verifyingContract: tokenAddress,
};
const types = {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
};
const permitData = {
owner: await signer.getAddress(),
spender,
value,
nonce,
deadline,
};
return signer.signTypedData(domain, types, permitData);
}Context
Implementing gasless approvals (EIP-2612 permit) or structured order signing
Revisions (0)
No revisions yet.