PM’s signer pre-arm gauntlet must confirm PM_SIGNER_KEY controls the configured maker_proxy. The original CREATE2-derivation check only knew two Polymarket factory schemes and silently failed for Deposit Wallet proxies, leaving live execution inert. Fix: verify the proxy’s on-chain owner() instead of deriving it.
For Agents
Code:
crates/position_manager/src/signer_arm.rs(arm_signer). New helpers:clob::proxy_owner_lower+ a pure decode helper, mirroring the reqwesteth_callpattern incrates/position_manager/src/onchain.rs. The check now readsmaker_proxy.owner()(selector0x8da5cb5b) overPOLYGON_RPC_URLand compares to the signer EOA; CREATE2 email-derivation kept as fallback.SigningClient::connectcarries no RPC URL (CLOB HTTP auth only) — the RPC call goes through the dedicated reqwest path.
Symptoms
- Log line:
signer pre-arm FAILED (proxy) — LIVE disabled, running unsigned. - Live execution silently inert: PM reserves, then fails
no signer, never POSTs an order, no money moves. - Reproduced on the “Bandi” account: proxy
0xdb0d23c2c3468da13c6e8abfe96edfecc72d9efe, owned on-chain by EOA0xfa7772b9aA1230a53D3BEA9a11f5c52f110684f7.
Root Cause — Polymarket has THREE wallet types, not two
The pre-arm proxy check CREATE2-derived the proxy from the signer EOA using only two factories vendored from rs-clob-client-v2 / polymarket-clob:
| Wallet type | Factory | Salt | Init-code hash |
|---|---|---|---|
| Email / Magic | 0xaB45c5A4B0c941a2F231C04C3f49182e1A254052 | keccak256(eoa) | PROXY_INIT_CODE_HASH |
| Gnosis Safe | 0xaacFeEa03eb1561C4e67d661e40682Bd20E3541b | keccak256(padded eoa) | SAFE_INIT_CODE_HASH |
The third type was unaccounted for:
The Deposit Wallet
Factory
0x00000000000Fb5C9ADea0298D729A0CB3823Cc07(“Polymarket: Deposit Wallet Factory Proxy”) deploys an EIP-1167 minimal proxy pointing at impl0x58ca52ebe0dadfdf531cde7062e76746de4db1eb(“Polymarket: Deposit Wallet Impl”). Its CREATE2 salt / init-code do not follow the standard EIP-1167 +keccak256(eoa)scheme — an off-chain reconstruction did not reproduce the address. So neither hardcoded factory can derive a Deposit Wallet proxy, and the pre-armproxycheck always failed for these accounts.
How it was root-caused
- Read the proxy’s creation tx (
0xe0965457…) on Polygonscan. - The
From/ factory address +WalletDeployed+OwnershipTransferredevents gave the deploying factory and the owner EOA. - Computed all four candidates off-chain (proxy ⨉ safe schemes ⨉ candidate EOAs) — none matched the real proxy address, confirming a non-standard derivation.
The Fix — verify ownership, don’t derive the address
Instead of replicating each factory’s CREATE2 (brittle, breaks on any new factory), the pre-arm now asks the chain who owns the proxy:
eth_callmaker_proxy.owner()(selector0x8da5cb5b) viaPOLYGON_RPC_URL.- Assert the returned address (lowercased)
== signer EOA. - CREATE2 email-derivation retained as a fallback.
- Added a diagnostic
warn!on failure loggingonchain_owner/derived_email_proxy/expected_proxy— public addresses only, never the key.
This is correct for all Polymarket wallet types (email/Magic, Gnosis Safe, Deposit Wallet, and any future type) and is not coupled to factory addresses or init-code hashes.
Operator Lesson
PM_SIGNER_KEYmust be the EOA private key that on-chain owns the configuredmaker_proxy. The signer EOA is NOT the proxy address — the proxy is a contract. Verify ownership via the proxy’sowner()/ theWalletDeployedevent, never by assuming a CREATE2 scheme.
Side Note — DRPC WSS flapping (env-only fix, same session)
The DRPC ogws WSS endpoint flapped ~1 reconnect/min (Connection reset without closing handshake). Fixed by swapping POLYGON_WSS_URL to Alchemy (wss://polygon-mainnet.g.alchemy.com/v2/...) for both PM self-watch and cohort firstmover — env-only, no code change. The self-watch logs subscription is narrow (filtered to the CTF Exchange + OrderFilled + watched-wallet topics), so Alchemy bandwidth is minimal.
Related
- position_manager — PM service overview / execution gate
- pmv2-source-id-3way-invariant-2026-06-23 — another silent fail-closed gotcha
- pmv2-crypto-source-rename-gotcha-2026-06-24 — body-driven resolution gotcha