Minimal EIP-712 typed-data hashing, tailored to Polymarket orders.
Polymarket's Order struct contains only static ABI types (uint256,
address, uint8, bytes32), so encoding a struct's data is simply each
field left-padded to a 32-byte word and concatenated — no dynamic-type or
nested-struct handling is required. This module deliberately implements only
that subset.
The pieces, in EIP-712 terms:
type_hash/1—keccak256(encodeType).hash_struct/2—keccak256(typeHash ‖ encodeData).domain_separator/4— thehashStructof the standardEIP712Domain.digest/2—keccak256(0x19 0x01 ‖ domainSeparator ‖ hashStruct), the 32-byte value that is finally signed.
Summary
Functions
Assembles the final EIP-712 signing digest from a domain separator and a struct
hash: keccak256(0x19 0x01 ‖ domainSeparator ‖ hashStruct).
Computes the domain separator for an EIP-712 domain without a verifying
contract (name, version, chain_id).
Computes the domain separator for the standard EIP-712 domain
(name, version, chain_id, verifying_contract).
Left-pads a 20-byte address to a 32-byte word.
Passes a 32-byte bytes32 value through unchanged (it is already one word).
Encodes a dynamic string value to its 32-byte EIP-712 word, keccak256(value).
Left-pads a uint8 to a 32-byte word.
Left-pads a non-negative uint256 to a 32-byte big-endian word.
Hashes a struct: keccak256(typeHash ‖ encodeData). encoded_fields is the
list of already-encoded 32-byte words, in the struct's declared field order.
The type hash for an EIP-712 struct: keccak256(encodeType). encode_type is
the canonical type string, e.g. "Order(uint256 salt,...)".
Functions
@spec digest(<<_::256>>, <<_::256>>) :: <<_::256>>
Assembles the final EIP-712 signing digest from a domain separator and a struct
hash: keccak256(0x19 0x01 ‖ domainSeparator ‖ hashStruct).
@spec domain_separator(binary(), binary(), non_neg_integer()) :: <<_::256>>
Computes the domain separator for an EIP-712 domain without a verifying
contract (name, version, chain_id).
A verifyingContract-less domain has a distinct type hash, so this is not the
same as domain_separator/4 with a zero address. Polymarket's ClobAuthDomain
(used for L1 API-key authentication) omits the verifying contract.
@spec domain_separator(binary(), binary(), non_neg_integer(), <<_::160>>) :: <<_::256>>
Computes the domain separator for the standard EIP-712 domain
(name, version, chain_id, verifying_contract).
@spec encode_address(<<_::160>>) :: <<_::256>>
Left-pads a 20-byte address to a 32-byte word.
@spec encode_bytes32(<<_::256>>) :: <<_::256>>
Passes a 32-byte bytes32 value through unchanged (it is already one word).
@spec encode_string(binary()) :: <<_::256>>
Encodes a dynamic string value to its 32-byte EIP-712 word, keccak256(value).
Per EIP-712, dynamic types (string, bytes) contribute the keccak-256 hash of
their contents rather than the value itself.
@spec encode_uint8(0..255) :: <<_::256>>
Left-pads a uint8 to a 32-byte word.
@spec encode_uint256(non_neg_integer()) :: <<_::256>>
Left-pads a non-negative uint256 to a 32-byte big-endian word.
@spec hash_struct(binary(), [<<_::256>>]) :: <<_::256>>
Hashes a struct: keccak256(typeHash ‖ encodeData). encoded_fields is the
list of already-encoded 32-byte words, in the struct's declared field order.
@spec type_hash(binary()) :: <<_::256>>
The type hash for an EIP-712 struct: keccak256(encodeType). encode_type is
the canonical type string, e.g. "Order(uint256 salt,...)".