Overview
EIP-712 is a typed structured data hashing and signing standard that enables human-readable message signatures with domain separation to prevent replay attacks across applications. Mainnet standard - De facto standard for off-chain message signing in wallets (MetaMask “Sign Typed Data”). Enables permit functions (gasless approvals), signatures for DEX orders, DAO votes, and account abstraction. Key concepts:- Domain separator: Prevents cross-application replays via contract address + chain ID binding
- Struct hashing: Recursive Keccak256 encoding of typed data structures
- Primary type: Top-level struct being signed (e.g., “Mail”, “Permit”, “Order”)
- Type hash: Keccak256 of type signature string for schema verification
Quick Start
Examples
- Basic Message - Hash and encode simple typed data
- Sign & Verify - Complete signing and verification flow
- Nested Structs - Working with nested type hierarchies
- Encode Values - Value encoding for different types
- Domain Separator - Replay attack prevention
- ERC-2612 Permit - Gasless token approvals
- DEX Order - Off-chain order book signatures
- DAO Vote - Gasless governance voting
- Meta-Transaction - Relayer-based gasless transactions
API Styles
Tevm provides two ways to use EIP-712:Standard API (Recommended)
Crypto dependencies auto-injected - simplest for most use cases:Factory API (Advanced)
Tree-shakeable with explicit crypto dependencies. Useful for custom crypto implementations or minimal bundle size:- All hash/encode methods:
keccak256 signTypedData:hashTypedData+secp256k1.signrecoverAddress:keccak256+secp256k1.recoverPublicKey+hashTypedDataverifyTypedData:recoverAddress
API Reference
Core Functions
hashTypedData(typedData: TypedData): Uint8Array
Hashes typed data according to EIP-712 specification. Returns 32-byte hash ready for signing.
signTypedData(typedData: TypedData, privateKey: Uint8Array): Signature
Signs typed data with ECDSA (secp256k1). Returns signature object with r, s, v components.
verifyTypedData(signature: Signature, typedData: TypedData, address: Address): boolean
Verifies signature matches expected signer address.
recoverAddress(signature: Signature, typedData: TypedData): Address
Recovers signer’s Ethereum address from signature.
Type Encoding
encodeType(primaryType: string, types: TypeDefinitions): string
Generates canonical type encoding string (includes nested types alphabetically).
hashType(primaryType: string, types: TypeDefinitions): Uint8Array
Returns keccak256 hash of type encoding.
encodeValue(type: string, value: any, types: TypeDefinitions): Uint8Array
Encodes a single value according to its type (returns 32 bytes).
encodeData(primaryType: string, message: Message, types: TypeDefinitions): Uint8Array
Encodes complete message data (typeHash + encoded field values).
hashStruct(primaryType: string, message: Message, types: TypeDefinitions): Uint8Array
Hashes encoded struct data.
Domain
EIP712.Domain.hash(domain: Domain): Uint8Array
Hashes domain separator (used internally by hashTypedData).
Utilities
validate(typedData: TypedData): void
Validates typed data structure. Throws on invalid data.
format(typedData: TypedData): string
Formats typed data for human-readable display.
Type System
EIP-712 supports all Solidity types:Elementary Types
- Integers:
uint8throughuint256(8-bit increments),int8throughint256 - Address:
address(20 bytes) - Boolean:
bool - Fixed bytes:
bytes1throughbytes32 - Dynamic bytes:
bytes - String:
string
Reference Types
- Arrays:
type[](dynamic),type[N](fixed-size) - Structs: Custom named types
Encoding Rules
- Atomic types (uint, int, address, bool, fixed bytes): Encoded in 32 bytes
- Dynamic types (string, bytes, arrays): Hashed with keccak256
- Structs: Recursively encoded and hashed
- Arrays: Elements encoded, concatenated, then hashed
Domain Separator
The domain separator prevents signature replay across different contracts, chains, or application versions:- Signatures are bound to specific contract/chain
- Prevents cross-contract replay attacks
- Enables safe signature portability
- User sees what app/contract they’re authorizing
Implementations
Tevm provides three implementation strategies for EIP-712:Native Zig (49KB)
High-performance implementation with minimal bundle impact:WASM Composition
Tree-shakeable WASM modules for custom crypto pipelines:TypeScript Reference
Pure TypeScript via ethers/viem for verification:Use Cases
Permit (ERC-2612): Gasless Token Approvals
Enable token approvals without gas via off-chain signatures. Users sign permit message, relayer submits to contract:DEX Orders: Off-Chain Order Books
Sign order intent for decentralized exchanges. Orders stored off-chain, settled on-chain when matched:DAO Votes: Off-Chain Governance
Collect votes via signatures, submit batch on-chain for gas efficiency:Account Abstraction: UserOperation Signatures
Sign ERC-4337 UserOperations for smart contract wallets:MetaMask Integration
EIP-712 is the standard for MetaMask’s typed data signing (eth_signTypedData_v4):Security Benefits
EIP-712 provides multiple security improvements over raw message signing:Human-Readable Signing
Users see structured data (amounts, addresses, purposes) instead of opaque hex strings. Prevents blind signing attacks where users unknowingly authorize malicious actions.Domain Binding
Domain separator cryptographically binds signatures to specific contract + chain:Replay Protection
Combining domain separator with nonces prevents signature reuse:Security Best Practices
1. Always Validate Typed Data
2. Verify Recovered Address
3. Use Deadlines
4. Include Nonces
Common Vulnerabilities
Signature Malleability: EIP-712 uses low-s canonicalization. Tevm enforces this automatically. Replay Attacks: Without domain separator + nonce, signatures replayed on forks/other contracts. Always include both. Type Confusion: Frontend types must exactly match contract ABI. Mismatch causes signature rejection. Missing Validation: Always callvalidate() before signing user-provided data to prevent malformed structures.
Implementation Notes
- Uses native secp256k1 signatures (deterministic, RFC 6979)
- Keccak256 for all hashing operations
- Compatible with eth_signTypedData_v4 (MetaMask)
- Follows EIP-712 specification exactly
- Type encoding includes nested types alphabetically

