This is the smart-contract-wallet companion to the rest of the V2 series: the Migration playbook, the errors troubleshooting page, the pUSD explainer, and the TS ethers→viem port.
Last verified: May 2026 against docs.polymarket.com/trading/deposit-wallets, the V2 SDK source, and open GitHub issues.
The four signature types in V2
The signatureType parameter tells the CLOB how to verify your order signature. V1 had three. V2 added a fourth. Here’s the full set:
| Value | Name | What it means | Wallet type |
|---|---|---|---|
0 | EOA | Plain ECDSA — the CLOB verifies your signature recovers to your address. | Standard EOA (MetaMask, hardware wallet, raw private key). |
1 | POLY_PROXY | The CLOB validates against a Polymarket-managed proxy contract. | Magic Link / Google / email signup users. |
2 | GNOSIS_SAFE | The CLOB validates against an existing pre-V2 Gnosis Safe. | Browser-wallet users who connected before April 28, 2026. |
3 | POLY_1271 | The CLOB delegates verification to the funder contract via IERC1271.isValidSignature(hash, signature). | Any smart-contract wallet that implements EIP-1271 — Polymarket’s deposit wallet, Safe (new), Coinbase Agentic Wallet, custom SCWs. |
The first three existed in V1. The fourth — POLY_1271 — is the architectural change. Where types 1 and 2 hardcoded specific wallet types Polymarket had integrated, type 3 is generic: the CLOB doesn’t care what kind of contract you’re using, only that the contract implements the EIP-1271 standard interface. That’s a meaningfully different stance.
Important: pre-V2 wallets keep their signature type. If you had a Safe pre-cutover with signature_type=2, that’s still the right value. Don’t “upgrade” to 3 — your Safe wasn’t deployed as a Polymarket deposit wallet, and switching breaks your auth.
What EIP-1271 actually unlocks
EIP-1271 defines a standard interface that lets contracts sign things in the same conceptual way that EOAs do. The spec is one function:
interface IERC1271 {
function isValidSignature(bytes32 hash, bytes memory signature)
external view returns (bytes4 magicValue);
}
When a contract wants to sign data — for example, a Safe with three owners agreeing to a trade, or a deposit wallet whose owner is delegated to a session key — there’s no private key on the contract itself. Instead, the contract exposes isValidSignature(hash, signature) and decides for itself whether the supplied signature is “valid” by whatever rules it wants (m-of-n multi-sig, session-key whitelist, time-locked permissions, social recovery, etc.). It returns a magic 4-byte value (0x1626ba7e) when valid, anything else when not.
For Polymarket, this matters because the CLOB no longer needs to know your wallet’s internals. The verification path is:
- Order comes in with
signatureType = 3andsigner = <smart-contract wallet address>. - The CLOB extracts the order hash.
- The CLOB calls
IERC1271(signer).isValidSignature(orderHash, wrappedSignature)on-chain. - If the contract returns the magic value, the order is valid. Otherwise rejected.
This is what makes the path generic. The CLOB can now accept orders from:
- A Safe (multi-sig — three owners signing).
- A deposit wallet (Polymarket-deployed proxy delegating to an EOA owner or session signer).
- A Coinbase Agentic Wallet (a Coinbase-deployed SCW with
isValidSignaturewired to the user’s hardware key or session policy). - A custom agent wallet built on top of ERC-4337 bundlers, account abstraction, or session keys.
If your wallet implements EIP-1271 correctly, it can post orders to Polymarket. That’s the architectural unlock.
ERC-7739: the safety wrapper
There’s a wrinkle worth knowing about. Raw EIP-1271 has a well-known replay vulnerability: a signature that’s valid for one purpose can sometimes be replayed for another. ERC-7739 is a complementary standard that wraps EIP-1271 signatures in a nested TypedDataSign envelope bound to the verifying contract’s own domain — so a signature valid for “this Safe’s batch on this chain” can’t be replayed elsewhere.
Polymarket uses ERC-7739 wrapping for POLY_1271 CLOB signatures (the wrapped signature is longer than a normal 65-byte ECDSA signature). The V2 SDKs build this wrapper for you when signatureType = SignatureTypeV2.POLY_1271 and a funderAddress is set. If you sign at the raw API level, you need to build it yourself — see the API users section of the deposit wallet docs for the nested-domain spec.
Polymarket’s deposit wallet: the reference implementation
The deposit wallet is Polymarket’s own EIP-1271 wallet. It’s a per-user ERC-1967 proxy, deployed by a factory contract, owned by an EOA you control. It’s purpose-built for the POLY_1271 flow and is what Polymarket steers new API users toward.
Architecture
Type: per-user ERC-1967 proxy (not a Safe, not Coinbase’s Smart Wallet — a custom contract).
Factory:
0x00000000000Fb5C9ADea0298D729A0CB3823Cc07on Polygon mainnet.Deterministic address: derived via CREATE2 from the factory + a salt computed from the owner address. The deposit wallet address is computable before deployment:
walletId = bytes32(owner) // owner left-padded to 32 bytes args = abi.encode(factory, walletId) salt = keccak256(args) bytecodeHash = SoladyLibClone.initCodeHashERC1967(implementation, args) depositWallet = CREATE2(factory, salt, bytecodeHash)Both SDKs expose helpers:
deriveDepositWalletAddress()(TypeScript),get_expected_deposit_wallet()(Python).Holds: pUSD (your trading collateral) and conditional tokens (your positions).
Signs: two different payload kinds —
WALLETbatches for on-chain actions, and CLOB orders for trading. Those signatures aren’t interchangeable.
Deployment
Deployment is a relayer operation — Polymarket’s Relayer pays the gas, no POL needed on the owner EOA. Submit a WALLET-CREATE request to the relayer (no user signature required on this body), and the factory deploys your proxy at the deterministic address:
# Python — via py-builder-relayer-client
from py_builder_relayer_client.client import RelayClient
relayer = RelayClient(
os.environ["RELAYER_URL"],
137,
os.environ["PRIVATE_KEY"], # owner EOA
builder_config,
)
deposit_wallet = relayer.get_expected_deposit_wallet()
response = relayer.deploy_deposit_wallet() # → WALLET-CREATE
confirmed = response.wait()
print("Deployed at:", deposit_wallet)
// TypeScript — via @polymarket/builder-relayer-client
import { RelayClient } from "@polymarket/builder-relayer-client";
const relayer = new RelayClient(relayerUrl, 137, walletClient, builderConfig);
const depositWalletAddress = await relayer.deriveDepositWalletAddress();
const response = await relayer.deployDepositWallet();
await response.wait();
Two signature flows
After deployment, the owner signs two distinct kinds of payloads against the deposit wallet:
1. WALLET batches — on-chain wallet actions (approvals, transfers, splits/merges of CTF positions). A normal 65-byte EIP-712 signature over the DepositWallet/Batch type, against the wallet’s own EIP-712 domain:
const domain = {
name: "DepositWallet",
version: "1",
chainId,
verifyingContract: depositWalletAddress,
};
const types = {
Call: [{name:"target", type:"address"}, {name:"value", type:"uint256"}, {name:"data", type:"bytes"}],
Batch: [{name:"wallet", type:"address"}, {name:"nonce", type:"uint256"},
{name:"deadline", type:"uint256"}, {name:"calls", type:"Call[]"}],
};
Submitted to the relayer as type: "WALLET", executed by the deposit wallet contract.
2. CLOB orders — trading. An ERC-7739-wrapped POLY_1271 signature over the V2 Order struct. The order’s maker and signer must both be the deposit wallet address. Submitted to the CLOB as signatureType = 3. The SDK builds the wrapped signature; the CLOB validates it via isValidSignature on the deposit wallet.
This split is important to internalize: the same EOA owner uses the same key to sign both, but the payloads are different shapes and they target different verifiers. Conflating them is the most common source of bugs (we’ve seen it in user reports — see the py-clob-client-v2 Errors page).
Funding and approvals (the gotcha)
The most common new-user mistake: funding the owner EOA instead of the deposit wallet. pUSD held on the EOA is irrelevant to the CLOB — orders are made by the deposit wallet, and the CLOB checks pUSD balance at the funder (deposit wallet) address. Same for allowances — approve() calls from the EOA do not approve the V2 Exchange contracts to move pUSD from the deposit wallet. Approvals must be submitted via a WALLET batch from the deposit wallet.
After funding and approvals, sync the CLOB’s cached view with signature_type = 3:
client.update_balance_allowance(
BalanceAllowanceParams(
asset_type=AssetType.COLLATERAL,
signature_type=SignatureTypeV2.POLY_1271,
)
)
Skip this step and the CLOB will report your balance as zero even though pUSD sits in the deposit wallet on-chain.
⚠ An honest note: the SDK currently breaks the documented flow
If you’re a new API user trying to follow Polymarket’s documented path — deposit wallet, signature_type=3 — there is an open SDK bug as of May 25, 2026 that blocks order placement. Both py-clob-client-v2 #70 and clob-client-v2 #65 describe the same root cause: the L1 authentication path (create_or_derive_api_key()) signs the auth message using the EOA’s address rather than wrapping it through the deposit wallet’s EIP-1271 path. So the API key gets registered against the EOA. When you then place an order with signer = deposit_wallet, the CLOB rejects it: {"error": "the order signer address has to be the address of the API KEY"}.
The deposit wallet infrastructure itself (deployment, funding, approvals, batches) works correctly — only the API-key creation path is broken. The fix is small (~50 lines per SDK to wire the existing ERC-7739 wrapper into the L1 auth path) and has been proposed in the issue, but as of writing no PR has merged.
Workaround for new users right now: sign up via email/Magic at polymarket.com, export the wallet’s private key, and use signature_type=1 (POLY_PROXY) with the proxy address as funder. This works because Polymarket’s own onboarding registers the API key against the proxy correctly, bypassing the SDK’s broken L1 path.
We’re tracking the issue and will update once it’s fixed. If your bot is operating on a pre-V2 Safe or Magic account, none of this affects you — sig types 1 and 2 are working normally.
Beyond deposit wallets: the broader SCW ecosystem
The deposit wallet is the path Polymarket steers you to, but POLY_1271 is a generic mechanism. Any EIP-1271-compliant smart-contract wallet can — in principle — be wired in. Here’s how the other major SCWs fit.
Gnosis Safe
Pre-V2 Safes deployed through Polymarket’s onboarding flow use signature_type=2 (POLY_GNOSIS_SAFE) — that’s a dedicated path the CLOB has special-cased for years. New Safes deployed today, outside Polymarket’s flow, would use signature_type=3 (POLY_1271) — because Safe implements EIP-1271 natively. The CLOB would call Safe.isValidSignature(orderHash, packedSig) and the Safe’s threshold logic would determine validity.
In practice, this requires the same API-key-binding fix as deposit wallets (you’d want your CLOB API key registered against the Safe address, not your EOA), so for now new Safes have the same #70 limitation. Once the SDK fix lands, this becomes the canonical path for multi-sig agent operators.
Coinbase Agentic Wallets
Coinbase’s Agentic Wallets (and the broader Smart Wallet line) are EIP-1271-compliant smart-contract wallets designed for agent use cases — session keys, scoped permissions, passkey signing. They can sign CLOB orders via POLY_1271 the same way: pass the agentic wallet address as funder, set signatureType=3, and the CLOB validates via isValidSignature.
For agent operators this is actually the more interesting path than deposit wallets: agentic wallets give you fine-grained permission scoping (e.g., “this session key can spend up to $100/day on Polymarket markets resolving by date X”), which is meaningful security infrastructure for autonomous agents. See Best Agent Wallet for Prediction Markets for the comparison.
Custom / ERC-4337 agent wallets
If you’ve built a custom SCW for your agent — your own session-key model, intent-based execution, custom signature recovery — POLY_1271 is the integration target. As long as your wallet implements isValidSignature(hash, signature) correctly and you wrap the order signature in the ERC-7739 envelope, the CLOB will validate against it.
The catch is the same as Safe and agentic wallets: the API-key-registration bug currently blocks the L1 flow. Until that’s fixed, you’re stuck either using a pre-V2 wallet type or implementing the ERC-7739-wrapped L1 auth yourself.
Why this matters for agents specifically
For human users, the EOA flow was usually fine — one user, one key, one wallet. For agents, smart-contract wallets are a different category of value:
- Session keys. An agent can be granted a session signing key with narrow scope (specific markets, dollar caps, time windows). The session key signs orders; the smart wallet’s
isValidSignatureenforces the scope. If the session key leaks, blast radius is bounded. EOAs cannot do this — a leaked private key gives full control. - Multi-sig and approval workflows. A bot operator running money for clients might require m-of-n approval for large positions. Safe + POLY_1271 makes this trivial; an EOA cannot.
- Delegated execution. An agent provider (think a copy-trading service) can sign orders on behalf of users without ever touching user EOAs — the user’s smart wallet authorizes the agent address; the agent signs and posts; the smart wallet validates.
- Account abstraction. Bundlers, paymasters, and intent-based execution all assume smart-contract wallets. POLY_1271 is the bridge between that world and Polymarket.
The architectural posture matters: pre-V2, Polymarket was an “EOAs and a few special proxies” platform. Post-V2, it’s “any smart wallet that implements ERC-1271.” That’s the kind of plumbing that agent infrastructure builds on top of.
What didn’t change
To avoid over-correcting your existing setup:
- Pre-V2 Safes — still
signature_type = 2. Don’t move them to 3. - Pre-V2 Magic/email accounts — still
signature_type = 1. Don’t move them to 3. - L1/L2 API auth — the
ClobAuthDomainEIP-712 used for API key derivation stays at version"1". You do not need to regenerate API keys. - The Relayer — gasless transactions still work via the Relayer Client, using your existing HMAC builder API key. The relayer’s wallet-deployment flow now supports
WALLET-CREATEfor deposit wallets in addition to the legacy Safe/proxy paths.
Changelog
| Date | Change |
|---|---|
| May 25, 2026 | Initial publication. Verified against docs.polymarket.com/trading/deposit-wallets, docs.polymarket.com/trading/gasless, the py-clob-client-v2 source, and open issues #70 / #53. |
Official Resources
- Polymarket Deposit Wallets — the canonical deposit-wallet reference
- Polymarket Gasless Transactions — the Relayer that pays for
WALLET-CREATEand batches - Polymarket Authentication — Signature Types — the 0/1/2/3 reference
- Migrating to CLOB V2 — the cutover this signature type shipped with
- EIP-1271 spec
- ERC-7739 spec
- py-builder-relayer-client · @polymarket/builder-relayer-client
AgentBets Guides
- Agent Wallet Comparison — Safe vs Coinbase Agentic vs deposit wallet
- Best Agent Wallet for Prediction Markets — the ecosystem ranked
- Polymarket Auth Troubleshooting — sig type mismatches and
INVALID_SIGNATURE - Migrating Your Polymarket Bot to CLOB V2 — the full V1→V2 playbook
- py-clob-client-v2 Errors — including the open #70 bug
- What Is pUSD? — the asset deposit wallets hold
- Coinbase Agentic Wallets Guide — agentic wallet onboarding
- Polymarket Coinbase Wallet Quickstart
- Polymarket TypeScript SDK Reference · py_clob_client Reference
Where This Fits in the Agent Betting Stack
This is Layer 2 (Wallet) ↔ Layer 3 (Trading) plumbing in the Agent Betting Stack. POLY_1271 is the bridge between how an agent holds money and signs (Layer 2) and how it puts orders on the wire (Layer 3). The architectural move — from “Polymarket accepts EOAs and a few special proxies” to “Polymarket accepts any ERC-1271 wallet” — is what makes things like session keys, agent-scoped permissions, and account-abstracted bot deployments concrete on Polymarket. Once the open SDK bug lands, the canonical path for new agent operators is a smart-contract wallet (deposit wallet, Safe, or an agentic wallet) signing CLOB orders through POLY_1271, with the Relayer paying gas in the background.
This guide is maintained by AgentBets.ai. Built something interesting on POLY_1271? Let us know on Twitter.
Not financial advice. Built for builders.
