gleeth/eip712

EIP-712 typed structured data hashing and signing.

Implements the encoding rules from the EIP-712 specification for signing structured data with domain separation. Used for ERC-2612 permits, DEX order signing, meta-transactions, and other off-chain authorization.

Examples

let domain = eip712.domain()
  |> eip712.domain_name("MyDapp")
  |> eip712.domain_version("1")
  |> eip712.domain_chain_id(1)

let types = dict.from_list([
  #("Mail", [
    eip712.field("from", "address"),
    eip712.field("to", "address"),
    eip712.field("contents", "string"),
  ]),
])

let message = dict.from_list([
  #("from", eip712.address_val("0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826")),
  #("to", eip712.address_val("0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB")),
  #("contents", eip712.string_val("Hello, Bob!")),
])

let data = eip712.typed_data(types, "Mail", domain, message)
let assert Ok(sig) = eip712.sign_typed_data(data, wallet)

Types

EIP-712 domain separator fields. All fields are optional - include only those relevant to your application.

pub type Domain {
  Domain(
    name: option.Option(String),
    version: option.Option(String),
    chain_id: option.Option(Int),
    verifying_contract: option.Option(String),
    salt: option.Option(String),
  )
}

Constructors

Complete EIP-712 typed data structure ready for hashing or signing.

pub type TypedData {
  TypedData(
    types: dict.Dict(String, List(TypedField)),
    primary_type: String,
    domain: Domain,
    message: dict.Dict(String, TypedValue),
  )
}

Constructors

A field in a typed struct definition.

pub type TypedField {
  TypedField(name: String, type_name: String)
}

Constructors

  • TypedField(name: String, type_name: String)

A value in a typed data message. Matches the EIP-712 encoding rules.

pub type TypedValue {
  StringVal(String)
  IntVal(Int)
  BoolVal(Bool)
  AddressVal(String)
  Bytes32Val(BitArray)
  BytesVal(BitArray)
  ArrayVal(List(TypedValue))
  StructVal(dict.Dict(String, TypedValue))
}

Constructors

  • StringVal(String)
  • IntVal(Int)
  • BoolVal(Bool)
  • AddressVal(String)
  • Bytes32Val(BitArray)
  • BytesVal(BitArray)
  • ArrayVal(List(TypedValue))
  • StructVal(dict.Dict(String, TypedValue))

Values

pub fn address_val(addr: String) -> TypedValue

Wrap an Ethereum address as a typed value.

pub fn array_val(items: List(TypedValue)) -> TypedValue

Wrap a list of typed values as an array value.

pub fn bool_val(b: Bool) -> TypedValue

Wrap a boolean as a typed value.

pub fn bytes32_val(b: BitArray) -> TypedValue

Wrap a fixed 32-byte value as a typed value.

pub fn bytes_val(b: BitArray) -> TypedValue

Wrap dynamic bytes as a typed value.

pub fn domain() -> Domain

Create an empty domain. Use domain_name, domain_version, etc. to set fields.

pub fn domain_chain_id(d: Domain, id: Int) -> Domain

Set the domain chain ID field.

pub fn domain_name(d: Domain, name: String) -> Domain

Set the domain name field.

pub fn domain_salt(d: Domain, salt: String) -> Domain

Set the domain salt field (hex-encoded bytes32).

pub fn domain_verifying_contract(
  d: Domain,
  address: String,
) -> Domain

Set the domain verifying contract address.

pub fn domain_version(d: Domain, version: String) -> Domain

Set the domain version field.

pub fn encode_type(
  type_name: String,
  types: dict.Dict(String, List(TypedField)),
) -> String

Build the canonical type encoding string. Example: “Mail(address from,address to,string contents)” Referenced structs are collected, sorted, and appended.

pub fn field(name: String, type_name: String) -> TypedField

Create a typed field definition.

pub fn hash_domain(
  d: Domain,
  custom_types: dict.Dict(String, List(TypedField)),
) -> Result(BitArray, String)

Compute the domain separator hash.

pub fn hash_struct(
  type_name: String,
  data: dict.Dict(String, TypedValue),
  types: dict.Dict(String, List(TypedField)),
) -> Result(BitArray, String)

Compute hashStruct: keccak256(typeHash || encodeData(s)).

pub fn hash_type(
  type_name: String,
  types: dict.Dict(String, List(TypedField)),
) -> BitArray

Compute typeHash: keccak256(encodeType(typeName)).

pub fn hash_typed_data(
  data: TypedData,
) -> Result(BitArray, String)

Compute the EIP-712 digest: keccak256(“\x19\x01” || domainSeparator || hashStruct(message)). This is the hash that gets signed.

pub fn int_val(n: Int) -> TypedValue

Wrap an integer as a typed value (for uint256, int256, etc.).

pub fn recover_typed_data(
  data: TypedData,
  signature_hex: String,
) -> Result(String, String)

Recover the signer address from a typed data signature.

pub fn sign_typed_data(
  data: TypedData,
  w: wallet.Wallet,
) -> Result(secp256k1.Signature, String)

Sign typed data with a wallet. Returns the signature.

pub fn string_val(s: String) -> TypedValue

Convenience constructors for typed values.

pub fn struct_val(
  fields: dict.Dict(String, TypedValue),
) -> TypedValue

Wrap a dict of named fields as a struct value.

pub fn typed_data(
  types: dict.Dict(String, List(TypedField)),
  primary_type: String,
  d: Domain,
  message: dict.Dict(String, TypedValue),
) -> TypedData

Create a complete typed data structure.

Search Document