O digest é keccak256(0x19 ‖ 0x01 ‖ domainSeparator ‖ hashStruct(message)). O hashStruct calcula o hash de um hash do tipo (o Keccak-256 de uma string de tipo canônica, como Mail(address from,address to,string contents)) junto com os campos codificados; strings, bytes e arrays entram como hash, e structs aninhadas têm o hash calculado recursivamente. O separador de domínio é hashStruct(EIP712Domain) com name, version, chainId, verifyingContract e, opcionalmente, salt; o hash do tipo do domínio usual de quatro campos é 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f.
As carteiras a acionam por eth_signTypedData_v4. Ela é a base do permit da EIP-2612, do Permit2 da Uniswap, dos hashes de transação do Safe, de livros de ofertas, de relayers para transações sem gás e de muitos sistemas de governança. Uma assinatura de dados tipados só é tão segura quanto o que o usuário entendeu: assinaturas permit off-chain que concedem allowances ilimitadas são uma das principais técnicas de phishing.
Bugs comuns: uma string de tipo que não corresponde exatamente à struct, um chainId ou verifyingContract ausente ou errado e assinaturas aceitas duas vezes porque o contrato não armazena nenhum nonce. O inspetor de EIP-712 recalcula todos os hashes localmente.