EIP-712 order hashing implementation for the polymarket-clob crate, covering alloy 1.6 import paths, domain params for Polymarket V2, clippy compliance patterns, and the OrderV2 non-exhaustive gotcha.

Files Changed

  • crates/polymarket-clob/src/lib.rs
  • crates/polymarket-clob/Cargo.toml

OrderV2 Non-Exhaustive Gotcha

polymarket_client_sdk_v2::clob::types::OrderV2 is generated by the sol! macro with #[non_exhaustive]. This prevents struct-expression construction — including functional record update syntax (..Default::default()) — from outside the defining crate. Attempting either form produces a compile error.

Workaround: construct via Default, then assign fields individually:

let mut o = OrderV2::default();
o.salt = salt;
o.maker = maker;
o.field = val;
o

FRU blocked by #[non_exhaustive]

OrderV2 { salt, ..Default::default() } does NOT compile from an external crate. Use individual field assignment on a Default instance instead.

EIP-712 Import Paths (alloy 1.6)

ItemCorrect import path
Eip712Domainalloy_sol_types::Eip712Domain
eip712_domain! macroalloy_sol_types::eip712_domain
SolStruct (.eip712_signing_hash())alloy_sol_types::SolStruct as _
Address, U256, B256polymarket_client_sdk_v2::types::{Address, B256, U256}

Wrong path trap

alloy::dyn_abi::Eip712Domain is a DIFFERENT type used internally by the vendored SDK. Do not confuse it with alloy_sol_types::Eip712Domain.

eip712_domain! Macro Syntax

The macro takes a verifying_contract: Address value (not a string) and a chain_id: u64 literal:

use alloy_sol_types::{eip712_domain, Eip712Domain};
 
eip712_domain! {
    name: "Polymarket CTF Exchange",
    version: "2",
    chain_id: 137,
    verifying_contract: address_value,
}

Polymarket V2 Domain Parameters

FieldValue
Name"Polymarket CTF Exchange"
Version"2"
Chain ID137 (Polygon mainnet)
Standard exchange0xE111180000d2663C0091e4f400237545B87B996B
Neg-risk exchange0xe2222d279d744050d28e00520010520000310F59

For Agents

These constants belong in crates/polymarket-clob/src/lib.rs. The verifying_contract changes depending on neg_risk: bool on the order — a non-deferrable runtime branch, never hardcode one side. See also pmv2-autotrade-engine-design for the V1/V2 landmine context.

Clippy Compliance for Const Address Parsing

expect_used and unwrap_used are forbidden in workspace lints. For hardcoded chain constants that require fallible parsing at initialization:

use std::sync::LazyLock;
use polymarket_client_sdk_v2::types::Address;
 
#[allow(clippy::unwrap_used)]
static STANDARD_EXCHANGE: LazyLock<Address> = LazyLock::new(|| {
    "0xE111180000d2663C0091e4f400237545B87B996B".parse().unwrap()
});

The #[allow] is scoped tightly to each LazyLock static. This panics at first use if the const string is malformed — acceptable for hardcoded chain constants. Do not add a crate-wide allow.

verifying_contract Helper Must Be const fn

Clippy’s missing_const_for_fn (nursery, promoted to deny via -D warnings) fires on a function that only branches over 'static str constants. Declare it const fn:

pub const fn verifying_contract(neg_risk: bool) -> &'static str {
    if neg_risk {
        "0xe2222d279d744050d28e00520010520000310F59"
    } else {
        "0xE111180000d2663C0091e4f400237545B87B996B"
    }
}

alloy-sol-types as a Separate Workspace Dep

The vendored rs-clob-client-v2 already depends on the alloy umbrella crate, but polymarket-clob must declare alloy-sol-types explicitly in its own Cargo.toml to directly access Eip712Domain, eip712_domain!, and SolStruct:

[dependencies]
alloy-sol-types = { workspace = true }

Add the corresponding workspace entry in the root Cargo.toml [workspace.dependencies] if not already present.