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.rscrates/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;
oFRU blocked by
#[non_exhaustive]
OrderV2 { salt, ..Default::default() }does NOT compile from an external crate. Use individual field assignment on aDefaultinstance instead.
EIP-712 Import Paths (alloy 1.6)
| Item | Correct import path |
|---|---|
Eip712Domain | alloy_sol_types::Eip712Domain |
eip712_domain! macro | alloy_sol_types::eip712_domain |
SolStruct (.eip712_signing_hash()) | alloy_sol_types::SolStruct as _ |
Address, U256, B256 | polymarket_client_sdk_v2::types::{Address, B256, U256} |
Wrong path trap
alloy::dyn_abi::Eip712Domainis a DIFFERENT type used internally by the vendored SDK. Do not confuse it withalloy_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
| Field | Value |
|---|---|
| Name | "Polymarket CTF Exchange" |
| Version | "2" |
| Chain ID | 137 (Polygon mainnet) |
| Standard exchange | 0xE111180000d2663C0091e4f400237545B87B996B |
| Neg-risk exchange | 0xe2222d279d744050d28e00520010520000310F59 |
For Agents
These constants belong in
crates/polymarket-clob/src/lib.rs. Theverifying_contractchanges depending onneg_risk: boolon 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.