{"id":"EIP-712","name":"Typed Structured Data Signing","status":"Final","chain":"both","category":{"id":"comprehension","name":"Comprehension & Display","description":"Making blockchain data readable to humans"},"journeyStages":[{"id":"authentication","name":"Authentication & Identity","description":"Proving who you are and logging in"},{"id":"reading","name":"Reading & Understanding","description":"Interpreting what you're being asked to do"}],"uxImpact":"EIP-712 standardizes hashing and signing for typed structured data. A supporting wallet can render fields such as an amount, deadline, and verifying contract more readably than an opaque digest, but the EIP does not require every wallet to display fields or provide replay protection. The app and verifier must bind the message and enforce its own safety rules.","officialUrl":"https://eips.ethereum.org/EIPS/eip-712","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"EIP-712","specConstraints":[{"id":"eip-712-domain-field-governance","constraint":"Dapp implementers should not add private EIP-712 domain fields; new fields should go through the EIP process.","appliesTo":"application","normativity":"normative","specQuote":"Dapp implementers should not add private fields, new fields should be proposed through the EIP process.","specSection":"Specification — Definition of domainSeparator","specUrl":"https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator","designImplication":"Keep the domain schema interoperable and explain any application-specific context outside the signed domain unless the field is standardized. Do not make wallet rendering depend on hidden private fields."},{"id":"eip-712-domain-separation","constraint":"Domain separation prevents otherwise identical typed structures from colliding across signing contexts.","appliesTo":"application","normativity":"rationale","specQuote":"The domain separator prevents collision of otherwise identical structures.","specSection":"Rationale — Rationale for domainSeparator","specUrl":"https://eips.ethereum.org/EIPS/eip-712#rationale-for-domainseparator","designImplication":"Show the relevant domain context in the app preview and signing review. Treat a typed message as scoped to its declared domain, not as a portable authorization without verification."},{"id":"eip-712-distinct-signing-uses","constraint":"A domain separator can distinguish multiple signature use cases that share the same struct instance.","appliesTo":"application","normativity":"rationale","specQuote":"The domain separator also allows for multiple distinct signatures use-cases on the same struct instance within a given DApp.","specSection":"Rationale — Rationale for domainSeparator","specUrl":"https://eips.ethereum.org/EIPS/eip-712#rationale-for-domainseparator","designImplication":"Give each authorization purpose a clear domain and explain the purpose-specific scope in the review surface. Do not reuse one generic confirmation label for distinct signature effects."},{"id":"eip-712-legacy-hash-danger","constraint":"Legacy struct hashing based on a memory address is dangerous and can make dependent dApps nondeterministic or broken.","appliesTo":"account-implementation","normativity":"backwards-compatibility","specQuote":"The Solidity expression `keccak256(someInstance)` for an instance `someInstance` of a struct type `SomeStruct` is valid syntax. It currently evaluates to the `keccak256` hash of the memory address of the instance. This behaviour should be considered dangerous. In some scenarios it will appear to work correctly but in others it will fail determinism and/or injectiveness. DApps that depend on the current behaviour should be considered dangerously broken.","specSection":"Backwards Compatibility","specUrl":"https://eips.ethereum.org/EIPS/eip-712#backwards-compatibility","designImplication":"Do not present legacy struct hashing as equivalent to EIP-712 typed signing. Audit existing signing and verification paths and show a clear fallback or migration state when the typed domain is not enforced."},{"id":"eip-712-replay-handling","constraint":"Applications authorizing actions with signed messages must reject repeated use or make the action idempotent.","appliesTo":"application","normativity":"security-considerations","specQuote":"It is _very important_ that implementers make sure the application behaves correctly when it sees the same signed message twice. For example, the repeated message should be rejected or the authorized action should be idempotent.","specSection":"Security Considerations — Replay attacks","specUrl":"https://eips.ethereum.org/EIPS/eip-712#replay-attacks","designImplication":"Show the nonce or consumed-message state when it affects user confidence, and make replayed or already-used signatures a first-class result rather than a generic failure. A deadline alone does not provide replay protection."},{"id":"eip-712-frontrunning-handling","constraint":"An application that broadcasts a signature for on-chain use must handle an attacker submitting it first.","appliesTo":"application","normativity":"security-considerations","specQuote":"When the signature is broadcast to a blockchain for use in a contract, the application has to be secure against frontrunning attacks.","specSection":"Security Considerations — Frontrunning attacks","specUrl":"https://eips.ethereum.org/EIPS/eip-712#frontrunning-attacks","designImplication":"Explain whether first submission consumes the authorization and make the already-submitted or unavailable state recoverable. Do not imply that readable typed data by itself prevents frontrunning."},{"id":"eip-712-chain-context","constraint":"A user-agent should refuse signing when domain chainId does not match the active chain.","appliesTo":"wallet","normativity":"normative","specSection":"Specification > Definition of domainSeparator","specUrl":"https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator","designImplication":"Show the active chain and signed domain chain together, and block or clearly explain a mismatch before the user signs.","specQuote":"* `uint256 chainId` the [EIP-155][EIP-155] chain id. The user-agent _should_ refuse signing if it does not match the currently active chain."},{"id":"eip-712-contract-phishing-context","constraint":"A user-agent may use verifyingContract for contract-specific phishing prevention.","appliesTo":"wallet","normativity":"normative","specSection":"Specification > Definition of domainSeparator","specUrl":"https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator","designImplication":"Show the verifying contract as signing context and use it for optional phishing warnings without implying the address alone proves contract safety.","specQuote":"* `address verifyingContract` the address of the contract that will verify the signature. The user-agent _may_ do contract specific phishing prevention."}],"summary":"EIP-712 standardizes hashing and signing for typed structured data. A supporting wallet can render fields such as an amount, deadline, and verifying contract more readably than an opaque digest, but the EIP does not require every wallet to display fields or provide replay protection. The app and verifier must bind the message and enforce its own safety rules.","applicability":{"whenToUse":["Users sign off-chain messages and wallets should show readable intent.","Permit, vote, or order flows need domain-bound typed data (EIP-712).","You must show amounts, tokens, deadlines, and contract context before sign."],"whenToAvoid":["Flows use on-chain transactions only with no typed-data signatures.","The signer may render typed fields differently from the app preview, and the verifier still needs to enforce the domain and replay rules.","Hardware wallets or platforms block structured signing reliably."]},"designerTakeaways":["Show typed fields in an app preview and let supporting wallets render them when they support structured signing.","Include and verify the relevant domain fields to restrict the chain and contract; track nonces or consumed messages to reject repeated use.","Add an enforced deadline and readable copy so stale signatures are easier to identify."],"mentalModel":[{"label":"User intent","description":"The dApp builds a structured message with amounts, deadlines, and permissions instead of opaque hex."},{"label":"Typed schema","description":"Field names and types define the typed message; a supporting wallet may render them, so use meaningful names and keep an app preview available."},{"label":"Domain binding","description":"Domain fields are optional. Include and verify chainId and verifyingContract when the authorization must be bound to them, and show the actual domain fields in the wallet. The verifying application must separately reject replayed messages."},{"label":"Wallet display","description":"A supporting wallet may parse and display typed fields. Your app preview should explain the same message without assuming a particular wallet renderer."},{"label":"Signature scope","description":"The signed message grants exactly what the fields describe, often off-chain until submitted. Include deadlines so stale signatures are obvious."}],"problemsSolved":[{"problem":"Users sign unreadable hex blobs","oldWay":"Sign \"0x4f8a3bc7d91e...\" — what does this even mean?","newWay":"A typed message can describe \"Swap 100 USDC for 0.05 ETH, deadline 10 minutes\" for a compatible signer or app preview","impact":"critical"},{"problem":"Can't verify what you're signing","oldWay":"Hope the dApp isn't lying about what that hex means","newWay":"A compatible wallet or app can render the typed fields; the verifier still checks the signed values and domain","impact":"critical"},{"problem":"Signatures replayable across contexts","oldWay":"Sign for Uniswap, attacker uses on SushiSwap","newWay":"A checked verifyingContract domain field can bind the signature to that contract","impact":"high"}],"uxPatterns":[{"name":"Typed Data Signature","description":"Structured, readable signature request","userFlow":["dApp requests EIP-712 signature","Wallet parses typed data structure","Displays fields in readable format","Shows domain/contract info","User verifies and signs"]},{"name":"Order Confirmation","description":"Trading orders with clear terms","userFlow":["User creates sell order","App generates EIP-712 typed order","Wallet shows order terms clearly","User signs off-chain order","Order stored, executed when matched"]}],"uiComponents":[{"name":"TypedDataViewer","description":"Renders EIP-712 typed data in readable format","states":["loading","parsed","error"],"props":["typedData","domain","primaryType"]},{"name":"FieldValuePair","description":"Single field:value row with type indicator","states":["normal","highlighted","warning"],"props":["name","value","type"]},{"name":"DomainVerifier","description":"Shows and verifies signing domain","states":["verified","unknown","suspicious"],"props":["name","chainId","verifyingContract"]}],"antiPatterns":[{"pattern":"Assuming EIP-712 guarantees readable wallet display","why":"Wallet support and rendering vary, and typed fields do not prove a contract or message is safe","instead":"Use typed data where it fits, provide an in-app preview, feature-detect signing support, and keep a clear fallback","severity":"critical"},{"pattern":"Poor field names in typed data","why":"\"a1\", \"b2\" mean nothing to users","instead":"Human-readable names: \"tokenAmount\", \"deadline\"","severity":"high"},{"pattern":"Not including deadline in signatures","why":"Without an enforced expiry, authorization can remain usable indefinitely. An expiry alone still permits replay before its deadline.","instead":"Include and enforce an appropriate deadline and a nonce or consumed-message check","severity":"high"},{"pattern":"Showing raw timestamps","why":"\"1707123456\" is not human-readable","instead":"Convert to \"Feb 5, 2024 at 3:30 PM\"","severity":"medium"}],"keyTakeaways":["EIP-712 defines typed structured hashing and signing; readable display depends on the signer and app.","Use typed data when its domain and schema fit the authorization, with a preview and fallback for unsupported wallets.","Use verifier-enforced nonces or consumption tracking for replay prevention; deadlines only limit the validity period.","Use human-readable field names","Format timestamps as readable dates"],"technicalNotes":"EIP-712 defines typed structured hashing and domain separation. Domain fields such as name, version, chainId and verifyingContract are optional; include and verify the fields required by the application. Wallets may expose eth_signTypedData_v4 when supported. The signed digest uses the 0x1901 prefix, domain separator and struct hash. The standard does not provide replay protection: the verifier must reject reused nonces or consumed messages, or make repeated execution harmless. An enforced deadline limits lifetime but does not prevent reuse before expiry. Readable fields do not prove the contract is safe.","lastUpdated":"2026-09-18","lastReviewed":"2026-09-18","sources":[{"label":"EIP-712 official specification","url":"https://eips.ethereum.org/EIPS/eip-712","type":"official-spec"}]},"sources":[{"label":"Official specification","url":"https://eips.ethereum.org/EIPS/eip-712","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=EIP-712","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/EIP-712","markdown":"https://www.eipsfordesigners.com/standards/EIP-712/content.md","agent":"https://www.eipsfordesigners.com/standards/EIP-712/agent.md","api":"https://www.eipsfordesigners.com/api/standards/EIP-712","official":"https://eips.ethereum.org/EIPS/eip-712","discussion":"https://ethereum-magicians.org/search?q=EIP-712"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}