All articles
Polymarket·May 10, 2026·6 min read

EIP-712 Signed Orders: How Polymarket's Gasless Trading Works

How Polymarket's EIP-712 signed orders enable gasless trading via off-chain signatures, proxy wallets, and relayer settlement on Polygon.

Every order you place on Polymarket is a signature, not a transaction. That distinction is the whole trick behind "gasless" trading, and it's also where a surprising number of trading bots quietly break.

What "gasless" actually means

Polymarket doesn't eliminate gas. It moves who pays it and when. Instead of you sending a transaction to place a limit order — which would cost MATIC and take a block confirmation before your order even existed — you sign a structured message off-chain with your wallet's private key. That signature is submitted to Polymarket's CLOB API, sits in the order book, and only touches the blockchain once it's matched against another order. At that point, Polymarket's operator wallet bundles the fill and pays the gas itself.

So the user cost of placing, editing, or cancelling an order is zero gas and roughly zero latency. The gas bill doesn't disappear, it just gets socialized into Polymarket's fee structure and paid by infrastructure the user never touches. If you've compared this to how a traditional AMM handles quotes, the mechanics are worth reading alongside our breakdown of the CLOB model versus AMM-based prediction markets, since the two designs make almost opposite tradeoffs on who pays for state changes and when.

EIP-712 in one paragraph

EIP-712 is an Ethereum standard for signing structured, typed data instead of an opaque hash. Rather than signing a blob of bytes that means nothing to a human, you sign a well-defined struct — field names, types, and a domain separator that ties the signature to a specific contract and chain. Wallets like MetaMask render this as a readable form ("You are signing an order to buy 100 YES shares at 0.62") instead of a wall of hex. The signature is a standard ECDSA signature (v, r, s) over the EIP-712 hash of that struct, and it's verifiable on-chain with ecrecover — cheaply, without needing the original transaction to exist yet.

The order struct

Polymarket's CTF Exchange contract defines an Order type roughly like this:

struct Order {
  uint256 salt;
  address maker;
  address signer;
  address taker;
  uint256 tokenId;
  uint256 makerAmount;
  uint256 takerAmount;
  uint256 expiration;
  uint256 nonce;
  uint256 feeRateBps;
  uint8 side;
  uint8 signatureType;
}

salt guarantees uniqueness so two identical orders don't collide. nonce lets a maker invalidate whole batches of orders in one on-chain call (their "cancel all" button). signatureType is the field most bot builders miss — it tells the exchange whether to verify against an EOA signature, a proxy-wallet signature, or an EIP-1271 contract signature, and picking the wrong one produces a silently rejected order rather than a helpful error.

Off-chain matching, on-chain settlement

Here's the actual flow end to end:

  1. You sign an Order struct with your wallet.
  2. The signed order goes to Polymarket's off-chain matching engine via the CLOB API — no gas, no block time.
  3. The engine matches it against a resting counter-order.
  4. Polymarket's operator relayer submits both signed orders to the CTF Exchange contract in one settlement transaction, paying gas from its own wallet.
  5. The contract verifies both signatures on-chain, checks balances/allowances, and atomically swaps conditional tokens for USDC.

Step 5 is the part people forget: the contract still checks real on-chain USDC balances and ERC-1155 conditional token balances at settlement time. Signing an order doesn't reserve funds. Nothing stops you from signing five orders against a balance that only covers three.

A worked example

Signing one of these orders with ethers.js looks like standard EIP-712 signing, nothing exotic:

const domain = {
  name: "Polymarket CTF Exchange",
  version: "1",
  chainId: 137,
  verifyingContract: EXCHANGE_ADDRESS,
};

const types = {
  Order: [
    { name: "salt", type: "uint256" },
    { name: "maker", type: "address" },
    { name: "signer", type: "address" },
    { name: "taker", type: "address" },
    { name: "tokenId", type: "uint256" },
    { name: "makerAmount", type: "uint256" },
    { name: "takerAmount", type: "uint256" },
    { name: "expiration", type: "uint256" },
    { name: "nonce", type: "uint256" },
    { name: "feeRateBps", type: "uint256" },
    { name: "side", type: "uint8" },
    { name: "signatureType", type: "uint8" },
  ],
};

const signature = await signer._signTypedData(domain, types, order);

That signature, plus the order fields, is the entire payload your bot needs to post a limit order. No RPC call to Polygon, no nonce management on your EOA, no gas estimation. It's why market-making strategies that quote and requote dozens of times a minute are viable on Polymarket at all — the same pattern our Polymarket market maker builds lean on to keep two-sided quotes fresh without bleeding gas on every reprice.

Where bots actually break this assumption

Balance races. Because signing doesn't lock funds, a bot that fires off multiple resting orders against the same USDC balance can get more filled than it has collateral for. You need your own off-chain accounting layer tracking "committed" balance across open orders, not just the on-chain balance.

Proxy wallet confusion. Most Polymarket users trade through a proxy contract (a Gnosis Safe variant), not their raw EOA, so maker and signer in the order struct are often different addresses. Bots that assume maker == signer generate orders that verify locally but get rejected by the exchange contract.

Nonce invalidation surprises. Calling the on-chain "cancel all" bumps your nonce and silently invalidates every resting signed order below it, including ones you meant to keep live. Sequencing this against active inventory in a copy-trading setup, like the logic behind a copytrading bot, needs explicit nonce tracking or you'll orphan orders without an error message telling you why.

Expiration and clock skew. Orders carry a Unix expiration. Bots generating orders with a server clock even a few seconds ahead can produce orders the matching engine treats as already expired, which looks like a phantom rejection until you check the timestamp math.

The one transaction you can't avoid. Gasless applies to order placement and cancellation, not to the initial USDC and conditional-token approvals. You still submit one real Polygon transaction per token to grant the Exchange contract allowance. Miss that step and every signed order fails at settlement with an approval error, not a signature error — a distinction that matters when you're debugging an arbitrage bot or a news-driven execution bot firing across many markets and token IDs where each one needs its own allowance.

The settlement layer also assumes markets resolve cleanly, which isn't guaranteed — worth pairing this with how UMA's optimistic oracle resolves Polymarket markets if your bot holds inventory through resolution. And if you're benchmarking this signature-based model against other venues, our comparison of Polymarket and Kalshi for algorithmic traders covers how differently each platform handles order lifecycle and settlement guarantees.

Gasless trading makes high-frequency quoting economically viable on Polymarket, but the tradeoff is that correctness now lives in your bot's balance and nonce bookkeeping instead of the chain enforcing it order by order. If your strategy depends on getting that bookkeeping right under load, talk to us about building a Polymarket market maker that handles the signature and settlement edge cases correctly from day one.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#Polymarket#EIP-712#gasless trading#CLOB#smart contracts