Documentation Index
Fetch the complete documentation index at: https://docs.polynode.dev/llms.txt
Use this file to discover all available pages before exploring further.
Deposit wallets supported. Polymarket is rolling out deposit wallets as a new account type for new users. The SDK handles both Safe and deposit wallet users automatically — no code changes needed. See the Deposit Wallets guide for details.
Overview
The trading module lets you place and manage orders on Polymarket. One function call handles wallet setup, credential creation, approvals, and order placement. You don’t need to understand Polymarket’s wallet types, contract addresses, or signing protocols. The SDK auto-detects whether a user has a Safe proxy, deposit wallet, or no wallet at all, and handles each case transparently.polynode-sdk >= 0.9.0. Deposit wallet support (address derivation, wallet detection) requires >= 0.10.0. Deposit wallet order signing (POLY_1271) requires >= 0.10.5.
Your project must use ESM imports (
import syntax). Add "type": "module" to your package.json, or use .mjs file extensions.Quick Start
Pick the path that matches your situation.I’m building a backend service (private keys on your server)
Generate wallets, manage them yourself. This is the simplest path.ensureReady() is idempotent. Call it every time your service starts. If the wallet is already set up, it returns instantly from the local database.
I’m building a platform with Privy (server wallets for each user)
UsecreatePrivySigner to trade with Privy-managed wallets. Each user gets their own wallet without touching private keys.
- Create a Privy app at dashboard.privy.io
- Enable Server wallets under Wallet infrastructure
- Generate an Authorization keypair (same section) — this is
PRIVY_AUTHORIZATION_KEY - Install:
npm install @privy-io/server-auth(the SDK imports it automatically viacreatePrivyClient)
I already have a Polymarket account (exported key or existing credentials)
If you export your private key from Polymarket, the SDK auto-detects your wallet type.Where to Send USDC.e
AfterensureReady(), send USDC.e to status.funderAddress. This is the address that holds your trading balance.
| Wallet type | funderAddress is… |
|---|---|
| Safe (default) | The Safe contract address (different from your private key’s address) |
| EOA | The wallet address itself |
Finding Markets
You need atokenId to place orders. Here’s how to find one:
GET /v1/events/search?q=bitcoin&limit=5 or GET /v1/crypto/active for live crypto markets.
Configuration
Builder Attribution (Your Own Credentials)
By default, orders placed through polynode are attributed to polynode’s builder profile on Polymarket. If you’re running a platform and want volume credited to your own builder account, pass your credentials inbuilderCredentials.
- Go to polymarket.com/settings?tab=builder and create a builder profile
- Generate API credentials (key, secret, passphrase)
- Pass them in
builderCredentialswhen constructing the trader
builderCredentials is set:
- All orders are attributed to your builder profile on the Builder Leaderboard
- Gasless Safe deployments and approvals use your builder account
- polynode never stores your builder credentials. They’re sent per-request and used only for HMAC signing.
builderCredentials is omitted, polynode’s default builder credentials are used. Your orders still go through, you just don’t get the builder attribution on your own profile.
Polymarket V2 Exchange
The Polymarket V2 exchange uses PolyUSD as collateral instead of USDC.e directly. One config change enables V2 support. Always callensureReady / ensure_ready before any V2 method — it deploys the Safe (if needed), sets V2 approvals, creates CLOB credentials, and refreshes the V2 CLOB’s balance-allowance cache:
1_000_000 = $1; TS uses bigint):
builder field on each V2 order. Mint one at polymarket.com/settings?tab=builder:
ensureReady() / ensure_ready() also refreshes the CLOB’s cached balance-allowance view after setting approvals. If you change approvals outside that flow, call trader.refreshBalanceAllowance() / trader.refresh_balance_allowance() before placing orders — otherwise the CLOB can reject with "not enough balance / allowance" until its cache catches up.
All other trading methods (order(), cancelOrder(), split(), merge(), etc.) work the same on V2 markets. The order_type accepts "GTC" (default — rest on book), "GTD" (with expiration unix seconds), "FOK" (fill-or-kill, fully match or cancel), or "FAK" (fill-and-kill, match what you can, cancel the rest). size is in shares of the outcome token, price is the per-share USD price between 0 and 1.
Fee Escrow
Charge your platform’s per-order fee via on-chain escrow. Fees are pulled before the order, distributed on fill, and refunded on cancel. This is your fee — independent of Polymarket’s protocol fee and V2 builder rev share (which are charged separately by the CLOB). See the Fee Escrow Guide for the full architecture and the three-fee breakdown.feeBps: 0 or omit feeConfig to skip your platform fee entirely. Zero overhead, zero behavior change. Note: this only turns off your fee — Polymarket’s protocol fee and V2 builder rev share are controlled independently by the CLOB.
API Reference
PolyNodeTrader.generateWallet()
Static async method. Generates a fresh wallet. You must await this call.
ensureReady(signer, opts?)
One-call onboarding. Detects wallet type (Safe, proxy, or deposit wallet), deploys contracts if needed, sets all approvals, creates CLOB credentials.
Signer types accepted:
string— hex private key (most common)createPrivySigner(...)— Privy server wallet- ethers v5/v6 Signer
- viem WalletClient
ReadyStatus with funderAddress (where to send collateral), signatureType, approvalsSet, credentials, and actions (what it did).
Wallet types detected automatically:
signatureType | Value | Description |
|---|---|---|
POLY_GNOSIS_SAFE | 2 | Gnosis Safe proxy (most existing Polymarket accounts) |
POLY_PROXY | 1 | Legacy proxy wallet |
POLY_1271 | 3 | Deposit wallet (newer Polymarket accounts, V2 only) |
EOA | 0 | Direct EOA signing |
POLY_1271), order signing uses the ERC-7739 TypedDataSign wrapper automatically. No code changes needed on your side — order() detects the wallet type from stored credentials and signs accordingly.
order(params)
Place an order on Polymarket.
cancelOrder(orderId) / cancelAll(market?)
Cancel a specific order or all orders.
split(params)
Split USDC into YES + NO outcome tokens. Gasless for Safe wallets. Auto-detects neg-risk vs standard markets.
merge(params)
Merge YES + NO outcome tokens back into USDC. Gasless for Safe wallets.
convert(params)
Convert NO positions on selected outcomes into USDC + YES on complementary outcomes. Only works on neg-risk multi-outcome markets. Gasless for Safe wallets.
checkBalance(wallet?)
Returns USDC.e and MATIC balance for the funder address.
checkApprovals(wallet?)
Check if all required token approvals are set on-chain (6 Polymarket approvals + 1 fee escrow approval).
getOpenOrders(params?)
Fetch open orders from the CLOB. Filters: market, assetId.
getOrderHistory(params?)
Query local order history from SQLite. Filters: limit, offset, tokenId, side.
linkCredentials(opts) / linkWallet(signer)
Import existing credentials or link a wallet manually.
exportWallet(wallet?) / exportAll() / importWallet(exported)
Export and import wallet state for backup. Private keys are never included.
unlinkWallet(address?) / getLinkedWallets()
Remove or list stored wallets.
close()
Close the SQLite connection. Call this when shutting down.
How It Works
- SDK signs the order locally (EIP-712)
- SDK sends to polynode’s relay at
trade.polynode.dev - Relay adds builder attribution and forwards to Polymarket’s CLOB
- Response returned to SDK, logged in local SQLite
fallbackDirect is true, orders go directly to Polymarket. Your trading never stops.
Wallet Model & Signing
Polymarket uses a two-address model for each trader:- EOA (signer) — a plain private key. Signs every order and every on-chain intent. Holds no trading funds.
- Safe (funder) — a Gnosis Safe smart wallet whose address is deterministically derived from the EOA (CREATE2 via the Polymarket Safe Factory). This is where USDC.e / pUSD / CTF positions live, and it’s the
makeron every order.
ensureReady(privateKey) on a fresh EOA does all four things for you, gasless via the Polymarket relayer:
- Derives the Safe address from the EOA via CREATE2
- Deploys the Safe if it doesn’t exist yet on-chain
- Sets all required approvals (USDC.e/pUSD to exchanges + collateral adapters, CTF to exchanges)
- Creates CLOB API credentials (L2 HMAC) by signing an EIP-712
ClobAuthmessage from the EOA
How orders are signed
Every V2 order struct includesmaker (the Safe address) and signer (the EOA address). The EOA signs the EIP-712 hash; the CLOB verifies the signature against signer and trusts that the signer is a 1-of-1 owner of the maker Safe (it is, because the Safe’s sole owner is the EOA by construction). At settlement time, the V2 exchange calls the Safe’s execTransaction with your signed payload as authorization.
How wrap/unwrap, approvals, and split/merge are signed
For any on-chain action that needs to move funds out of the Safe (wrapToPolyUsd, unwrapFromPolyUsd, setApprovals, split, merge, convert), the SDK:
- Builds the raw calldata for the target contract call
- Wraps it in a Safe
execTransactionEIP-712 payload with the current Safe nonce - Has your EOA
personal_signthat payload (via eth_account / alloy / ethers) - POSTs the signed payload to
https://relayer-v2.polymarket.com/submitwith your builder HMAC headers - Polls the relayer until the tx is mined on Polygon
wrapToPolyUsd() / unwrapFromPolyUsd() require builderCredentials in TraderConfig for Safe wallets — the HMAC authenticates your submit to the relayer.
Linking credentials vs ensureReady
Three ways to get a wallet ready to trade, depending on what you already have:| Situation | Call | What it does |
|---|---|---|
| Fresh EOA (no Safe yet) | trader.ensureReady(privateKey) | Derives Safe, deploys, approves, creates CLOB creds, stores in local DB |
| Existing EOA + Safe already set up (you re-start your service) | trader.ensureReady(privateKey) | No-op if the local DB already has creds; otherwise re-derives and re-fetches |
| You already have CLOB creds from somewhere else (DB export, Polymarket account) | trader.linkCredentials({ wallet, apiKey, apiSecret, apiPassphrase, signatureType, funderAddress }) then trader.linkWallet(privateKey) to attach the signer | Imports the creds into the local DB and attaches the signer for order signing. Skips approvals + deploys entirely. |
| Multi-user platform (one SDK instance, many users) | trader.linkCredentials(userA) → trader.linkWallet(userA.pk) → trade; then switch with trader.linkWallet(userB.pk) | Each linkWallet call swaps the active signer. linkCredentials writes into the local DB so you can return to a user later by calling linkWallet again with their key. Consider using one PolyNodeTrader per user for concurrency. |
Multi-user / server wallet patterns
For a backend that trades on behalf of many users:- Privy / server-managed wallets: use
createPrivySigner(privy, walletId, address)instead of passing a raw private key toensureReady. Same onboarding flow, but Privy holds the key and signs remotely. - Your own KMS: implement the
RouterSignerinterface (orTradingSignerin Rust / a signer callable in Python). The SDK only needs two operations from you:getAddress()andsignTypedData(payload). Everything else is local. - Per-user DB file: pass a different
dbPathper user to keep their credentials isolated on disk. Or use a shared DB and calllinkWallet(userPk)to swap active signers.
Credential Custody
Credentials are stored in a local SQLite database. The SDK never sends your private key or CLOB credentials to polynode’s servers. Back up withexportWallet().

