Skip to content

EIP712++ (single-header) — Typed-data hashing & signing for Ethereum

Project goal EIP712++ is a compact, proprietary C++ header that computes EIP-712 typed-data hashes and produces the EIP-191 digest ready for (r,s,v) signing. It gives you domain separators, hashStruct(...), and ergonomic encoders for Solidity types—so you can implement Permit (EIP-2612), meta-transactions, and off-chain authorizations without boilerplate.


Why it exists

  • Typed-data is easy to get wrong. Field order, dynamic types, and hashing rules are strict; small mistakes = invalid signatures.
  • You want a minimal, auditable API. One header, clear functions, predictable bytes in/bytes out.
  • Common flows should be one-liners. domain → struct hash → EIP-191 digest → sign → (r,s,v).

Features

  • Domain separator Compute EIP712Domain(...) hash for (name, chainId, verifyingContract, …).

  • hashStruct(...) for messages Variadic encoding + Keccak-256 per spec: hashStruct(T) = keccak(typeHash || encodeData(T)).

  • Solidity-type encoding Atomic types (bytes1..32, uint8..256, int8..256, bool, address) via compile-time traits; dynamic bytes/string are hashed (keccak(contents)), as required by EIP-712.

  • Final signing digest Produce keccak("\x19\x01" || domainSeparator || hashStruct(message)) (EIP-191).

  • Interop with your signer Outputs raw byte strings (bytes_t) that plug into your existing (r,s,v) signing stack (e.g., secp256k1 ECDSA).

  • Single header Drop-in, exception-based errors, zero ceremony.


Minimal API (at a glance)

namespace eip712 {
  using bytes_t = std::basic_string<uint8_t>;

  // Atomic & dynamic encoders
  bytes_t encode(const std::string& s);     // keccak(s)
  bytes_t encode(const bytes_t& b);         // identity
  template<class T> bytes_t encode(const T& v); // static Solidity types

  // Variadic struct encoding / hashing
  template<class... args_t>
  bytes_t encode(const std::string& types, args_t&&... args);

  template<class... args_t>
  bytes_t hash(const std::string& types, args_t&&... args); // keccak(typeHash || encodeData)
}

Example — ERC-20 Permit (EIP-2612)

#include "eip712.hpp"        // this header
#include "openssl.hpp"       // your keccak + signer
using bytes_t = eip712::bytes_t;

// 1) Domain separator
// EIP712Domain(string name,uint256 chainId,address verifyingContract)
bytes_t domain_sep = eip712::hash(
  "EIP712Domain(string name,uint256 chainId,address verifyingContract)",
  std::string("MyToken"),              // name
  solidity::u256_t{1},                 // chainId = 1
  solidity::address_t{"0xabc...def"}   // ERC-20 contract
);

// 2) Message struct hash
// Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)
bytes_t permit_hash = eip712::hash(
  "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)",
  solidity::address_t{"0xdead...beef"},  // owner
  solidity::address_t{"0xfeed...cafe"},  // spender
  solidity::u256_t{"1000000000000000000"}, // value = 1e18
  solidity::u256_t{42},                    // nonce
  solidity::u256_t{1699999999}             // deadline (unix)
);

// 3) EIP-191 digest to sign
bytes_t digest = openssl::keccak(
  std::string("\x19\x01", 2) + domain_sep + permit_hash
);

// 4) Sign (secp256k1 ECDSA) and recover
auto [r, s, v] = openssl::sign(digest, private_key_bytes);
auto pub       = openssl::ecrecover(digest, v[0], r, s);

Notes: • solidity::address_t / u256_t stand for your static-type wrappers that map to Solidity encodings. • string/bytes fields are hashed (keccak) as specified by EIP-712. • The exact type string must match the Solidity struct, including field order.

separation** — domain vs. message hashes kept explicit to avoid cross-domain replay.

Typical uses

  • Permit / Meta-tx: gasless approvals and relayed calls.
  • Off-chain authz: sign intents that execute on-chain later.
  • Custom typed messages: safe UX prompts in wallets.

Licensing & availability Proprietary, single-header library. Commercial licensing via Varga Consulting (Toronto, Canada). Contact: info@vargaconsulting.ca.