EIP-712: Typed structured data hashing and signing

article Remco Bloemen, Leonid Logvinov, Jacob Evans

The canonical Ethereum standard for hashing and signing typed structured data. Created in September 2017, EIP-712 replaced opaque hex-string message signing with human-readable, type-safe, domain-scoped signatures. It is the foundation for every modern off-chain signature pattern in the Ethereum ecosystem — meta-transactions, gasless approvals (EIP-2612 permit), Safe multisig signing, Farcaster key registration, and virtually all DApp login flows.

Core contributions

Typed structured data (𝕊): Extends the set of signable messages from transactions and raw bytestrings to structured data resembling Solidity structs. Signable messages become 𝕋 ∪ 𝔹⁸ⁿ ∪ 𝕊, with the new case distinguished by a leading \x19\x01 prefix that makes the encoding injective.

hashStruct algorithm: hashStruct(s) = keccak256(typeHash ‖ encodeData(s)) where typeHash = keccak256(encodeType(typeOf(s))). Each struct member is encoded to exactly 32 bytes — atomic values padded, dynamic bytes/string hashed, arrays hashed from their concatenated encodings, and nested structs encoded recursively as their hashStruct value. This makes hashStruct injective per type and cheap to implement in Solidity via abi.encode.

encodeType canonical form: Name(type1 member1,type2 member2,...) with referenced struct types appended alphabetically. Example: Transaction(Person from,Person to,Asset tx)Asset(address token,uint256 amount)Person(address wallet,string name). Computable at compile time and stored as a Solidity constant.

Domain separator: domainSeparator = hashStruct(EIP712Domain) where EIP712Domain is a struct with optional fields name, version, chainId, verifyingContract, and salt. Binds every signature to a specific protocol, version, chain, and contract, preventing cross-DApp and cross-chain signature collision. Enables multiple distinct signing purposes within a single struct type via different domain separators.

Final encoding: sign(keccak256("\x19\x01" ‖ domainSeparator ‖ hashStruct(message))). Compliant with ERC-191 (version byte 0x01).

eth_signTypedData JSON-RPC method: Wallet RPC for signing typed data. The wallet displays field names and values to the user, replacing blind hex signing with verifiable, human-readable consent.

Security properties

  • Determinism: Encoding is deterministic because all components are.
  • Injectivity: The leading-byte disambiguation ensures no valid transaction or \x19Ethereum Signed Message encoding can collide with a typed data encoding.
  • Type separation: typeHash prefix means encodeData only needs to be injective within a single type.
  • Domain separation: Signatures are scoped to (chain, contract, name, version) and cannot be replayed across domains.
  • Out of scope: EIP-712 does NOT provide replay protection within a domain (applications must use nonces or idempotency), and does NOT prevent frontrunning (applications must design around it).

Design rationales (rejected alternatives)

  • ABIv2 function signatures as typeHash: bytes4 is too small; even 256-bit ABIv2 signatures collide between ERC-20 and ERC-721 transfer(address,uint256). EIP-712 favors incompatibility where a hashing standard should.
  • Tight packing: Minimizes bytes but requires complex EVM instructions and prevents in-place computation.
  • ABIv2 abi.encode directly: Non-deterministic (multiple valid encodings of the same data).
  • Cyclical data structures: Would require path-dependent hashing, breaking composability. EIP-712 is optimized for tree-shaped data only.

Ecosystem impact

EIP-712 is the signing standard underneath:

  • ERC-2612 permit — gasless ERC-20 approvals
  • EIP-1271 contract signatures — the bridge that lets smart accounts (e.g., Safe) participate in EIP-712 flows
  • EIP-4361 “Sign-In with Ethereum”
  • OpenSea orders, Seaport, 0x orders
  • Farcaster’s SignedKeyRequest (see the blog post on creating a Farcaster account by hand)
  • Safe transaction hashing and SafeMessage wrapping
  • Meta-transaction relayers and account abstraction flows

Anything that signs a structured message off-chain and verifies it on-chain is built on this standard.

Ingestion manifest

MOC updated: Digital Identity

https://eips.ethereum.org/EIPS/eip-712