UtilityToolsLab

© 2026 UtilityToolsLab. Built and maintained by the UtilityToolsLab Team.

About·Changelog·Privacy Policy·Terms of Service·Contact
HomeSmart Contracts & EVMEIP-712 Hash

Related Tools

Function SelectorGas Fee CalculatorENS NamehashMerkle Tree BuilderABI EncoderChain ID ReferenceETH Tx Decoder

EIP-712 Typed Data Hash Calculator

Paste eth_signTypedData_v4 JSON to get the EIP-712 digest, domain separator, typeHash and every field encoding. Check a wallet prompt without signing.

You Might Also Like

All Smart Contracts & EVM

Function Selector

Get the 4-byte Ethereum function selector for any Solidity signature, e.g. transfer(address,uint256) → 0xa9059cbb. Keccak-256, runs in your browser.

Gas Fee Calculator

How much will an Ethereum transaction cost? Enter gas units and fees to get the cost in Gwei, ETH and USD, in EIP-1559 or legacy mode, with 6 presets.

ENS Namehash

Compute the ENS namehash (EIP-137) for any .eth domain, label by label, using Keccak-256 in your browser. See every recursive hashing step.

Merkle Tree Builder

Build a Keccak-256 Merkle tree from an address allowlist, read off the root, and copy an inclusion proof for any leaf. OpenZeppelin sorted pairs.

When a dapp asks your wallet to “sign typed data”, the wallet does not sign the JSON you see. It hashes it first, in a fixed order set by EIP-712, and signs a single 32-byte digest. If a contract computes that digest even slightly differently, every signature fails with nothing more helpful than “invalid signature”. This calculator shows each step of the hash.

Paste the object a dapp passes to eth_signTypedData_v4, with its types, primaryType, domain and message. You get the digest a wallet signs, the domain separator, hashStruct of the message, the typeHash and the encodeType string behind it, and the 32-byte word every field turns into. Nothing is signed and no wallet is connected, so it is also a safe way to see what a signature request actually commits you to.

Worked Example: The EIP's Own Mail Message

The tool opens with the example from the EIP-712 specification: a Mail from Cow to Bob reading "Hello, Bob!", on the “Ether Mail” domain at chain 1. The encodeType reads Mail(Person from,Person to,string contents)Person(string name,address wallet), the domain separator is 0xf2cee375…a912090f, and the digest is 0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2, the same value the specification publishes. Press Load Sample to switch to an ERC-2612 Permit on the USDC contract. That is the gasless-approval message most wallets show, and its digest is 0x106980d0…3ede1bd9d7.

Two outputs are easy to miss. Solidity constant gives a ready line such as bytes32 constant MAIL_TYPEHASH = keccak256("…"); for the verifying contract. The field tables let you copy any single encoded word, which is the fastest way to find the one field where your contract and your frontend disagree.

Encoding Method, Field by Field

  • Numbers, bool, address: one 32-byte word, left-padded; negative intN values use two's complement.
  • bytes1 to bytes32: right-padded to 32 bytes, and the value must be exactly N bytes long.
  • string and bytes: replaced by their keccak256, so a 5,000-character string still takes one word.
  • Structs: replaced by their own hashStruct, which is keccak256(typeHash ‖ encoded fields).
  • Arrays: keccak256 of every item's encoding laid end to end.

The digest is keccak256 of 0x19 0x01, the domain separator and hashStruct(message). Results were checked against the ethers.js 6.13.4 TypedDataEncoder on 2,000 randomly generated schemas and messages, and a signature made by ethers recovered to the same key from this digest.

Edge Cases That Change the Digest

  • Large numbers: a JSON number above 9,007,199,254,740,991 loses its last digits before any code sees it. Instead of hashing a different value, the tool stops with, for example, “"value" is a number above 9,007,199,254,740,991, which JSON numbers cannot hold exactly. Put it in quotes as a string.”
  • No EIP712Domain in types: the domain type is built from the keys present, in the order name, version, chainId, verifyingContract, salt, as ethers does. If EIP712Domain is given but the domain has a key it does not list, an amber note warns that the key is not hashed.
  • No primaryType: the one struct that no other type refers to is used, and the result says so. If two could qualify, you are asked to name it.
  • Extra message keys are ignored by the standard and listed in a note. A missing field is an error, not a zero.
  • Mixed-case addresses must pass the EIP-55 checksum, and plain uint is rejected because EIP-712 needs the full name, uint256.

Big numbers go in quotes: "1000000000000000000".

Digest a wallet signs
0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2

keccak256(0x19 ‖ 0x01 ‖ domain separator ‖ hashStruct(message)). This is what ecrecover is given on-chain.

Domain separator
0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f

EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)

hashStruct(Mail)
0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e

keccak256(typeHash ‖ encoded fields)

typeHash
0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2

keccak256 of the encodeType string below

encodeType
Mail(Person from,Person to,string contents)Person(string name,address wallet)

Referenced structs follow the primary type, sorted by name.

Solidity constant
bytes32 constant MAIL_TYPEHASH = keccak256("Mail(Person from,Person to,string contents)Person(string name,address wallet)");

Paste into the verifying contract.

Domain fields → 32-byte words
name string
0xc70ef06638535b4881fafcac8287e210e3769ff1a8e91f1b95d6246e61e4d3c6

keccak256 of 10-byte UTF-8 string

version string
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6

keccak256 of 1-byte UTF-8 string

chainId uint256
0x0000000000000000000000000000000000000000000000000000000000000001

uint256, left-padded to 32 bytes

verifyingContract address
0x000000000000000000000000cccccccccccccccccccccccccccccccccccccccc

address, left-padded to 32 bytes

Mail fields → 32-byte words
from Person
0xfc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8

hashStruct(Person)

to Person
0xcd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1

hashStruct(Person)

contents string
0xb5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8

keccak256 of 11-byte UTF-8 string

Hashing only: nothing is signed, no wallet is connected and no key is asked for. Compare the digest here with what a dapp asks your wallet to sign before you approve it.