signTypedData_v4 returns a wrong signature when the EIP-712 types entry omits a message field (Seaport totalOriginalConsiderationItems)
Symptom: EIP-712 signing works for one payload and produces a signature the verifying contract rejects for another. No error is thrown anywhere; recovery just yields the wrong address. Seen with Seaport OrderComponents signed from web3j, and it reproduces with any v4 signer (MetaMask, ethers, viem).
Cause: the digest is computed only from the fields declared in types. Any extra key present in message but absent from the struct definition is silently ignored, and any missing key changes the type string and therefore the typehash. In the failing case message contained totalOriginalConsiderationItems while the OrderComponents type list did not, so the client hashed an 11-field struct while Seaport hashes a 12-field one. Both sides produce well-formed signatures over different digests.
Fix: make types mirror the contract's typehash exactly — same fields, same order, same Solidity types. For Seaport 1.5, OrderComponents is offerer, zone, offer (OfferItem[]), consideration (ConsiderationItem[]), orderType, startTime, endTime, zoneHash, salt, conduitKey, totalOriginalConsiderationItems, counter. Field order is part of the encoding, not cosmetic.
Related silent-mismatch causes worth checking in the same pass: chainId supplied as a string vs a number (both are accepted by some libraries and encoded differently by others), a verifyingContract with wrong checksum casing, salt or version present in the contract's EIP712Domain but omitted from yours, and nested struct types that must themselves be declared and are hashed in alphabetical order of type name when building the encoded type string.
Debugging technique: compute the struct hash and domain separator on both sides and compare — _domainSeparatorV4() / the contract's typehash constant against your library's TypedDataEncoder.hash(domain, types, value) (ethers) — rather than comparing signatures.
Source: adapted from a Stack Exchange answer by Mila A (question by luntik), https://ethereum.stackexchange.com/questions/161595/signtypeddata-v4 — CC BY-SA 4.0