Skip to main content
Hosted trading is PMXT’s default execution path. You provide an API key and a wallet; your USDC sits in a non-custodial PreFundedEscrow smart contract on Polygon that can only move funds against an EIP-712 signature from your wallet; orders are built server-side, signed in your browser/server with your private key, and submitted by PMXT to the underlying venue. You never run a local server, never integrate with venue-specific signature schemes, and never expose your private key over the wire.
One chain to fund: Polygon. Hosted trading for Polymarket, Opinion, and Limitless uses a single Polygon-based PreFundedEscrow contract. Fund USDC on Polygon once; the same EVM wallet/key can trade the currently hosted venues. For non-Polygon hosted venues like Opinion (on BSC) and Limitless (on Base), PMXT handles cross-chain settlement transparently — you never sign anything on a chain other than Polygon.

When hosted mode is right

Hosted is the right default for:
  • Web and mobile apps — your backend holds the pmxt_api_key; users keep their own private keys on their devices.
  • Trading bots that don’t need sub-100ms latency — typical hosted round-trip is ~150–300ms including the venue submit.
  • Multi-venue strategies — you stay on one HTTP surface even when you’re trading across Polymarket, Opinion, and Limitless.
  • Anyone who doesn’t want to operate infrastructure.
A hosted trade has three actors: your client (holding the pmxt_api_key and, for writes, the user’s private_key), trade.pmxt.dev (PMXT’s hosted trading API — routes orders, operates against the escrow contract under user-signed authorization, talks to the venue), and the venue (Polymarket’s CLOB, Opinion’s matching engine, etc. — sees PMXT as the submitter). Reads (fetch_balance, fetch_positions, fetch_my_trades, etc.) only need the API key and a wallet address. Writes (create_order, cancel_order) require the user to sign an EIP-712 typed-data payload locally.

The trade flow

Step-by-step:
  1. Build. The SDK calls POST /v0/trade/build-order with the outcome target, side, and amount. Catalog UUIDs go on the wire as outcome_id; venue-native outcomes go on the wire as venue + venue_outcome_id. The server resolves the venue-native fields (token IDs, salt, expiry, fees), packages them into the venue’s EIP-712 typed-data shape, and returns a built_order_id plus the payload to sign.
  2. Sign. The SDK signs the typed-data payload locally with your private_key. This step never leaves your process. See Signing for the exact shape.
  3. Submit. The SDK calls POST /v0/trade/submit-order with the built_order_id and the signature. The server attaches the signature to the prepared order and submits to the venue.
  4. Settle. The venue matches the order. Settlement model varies by venue:
    • Polymarket — same-chain on Polygon. The user signs one EIP-712 OrderParams against the Polygon PreFundedEscrow domain. Settlement is direct against the Polymarket CTF exchange. No oracle, no cross-chain hop.
    • Opinion BUY — cross-chain Polygon → BSC, single signature + oracle-attested delivery-versus-payment (DvP). The user signs one CrossChainOrderParams payload on the Polygon PreFundedEscrow domain, authorizing a capped spend of their USDC. The operator fronts BSC working capital, buys on Opinion’s CLOB, and calls depositForUser on the BSC VenueEscrow to credit the user with the real outcome tokens. An independent settlement oracle observes the BSC delivery and signs a DeliveryAttestation. The operator submits the attestation to settleCrossChainBuy on Polygon, which verifies the user signature, the attestation binding (order, user, token, destination escrow), the oracle signature, a non-zero tokensDelivered, and a per-token worst-price check before releasing the user’s USDC. The operator cannot pull USDC until it has provably delivered tokens. Trust assumption: honest oracle.
    • Opinion SELL — cross-chain BSC → Polygon, dual-signed parallel, no oracle. The user signs two EIP-712 payloads at build time: a Polygon pay leg (CrossChainSellPayParams on the PreFundedEscrow domain) and a BSC pull leg (CrossChainSellPullParams on the VenueEscrow domain). The operator fires both in parallel: settleCrossChainSellUSDC credits the user’s USDC on Polygon, settleCrossChainSellTokens pulls the outcome tokens from the user’s BSC escrow. Time-to-tokens is roughly one block. The 2×2 outcome matrix’s row 3 (pay failed, pull succeeded) is the operator-trusted gap today; a future bond backstop will close it.
    • Limitless — cross-chain Polygon → Base, v1 signature-gated front-and-reimburse (ERC-7683). The user signs one ERC-7683 order via EIP-712 on the Polygon escrow domain: “buy ≥Y tokens, debit ≤X USDC, worstPrice, deadline, nonce”. The operator fronts Base working capital, buys on Limitless’ CLOB, delivers the real tokens to the user’s Base escrow, then pulls the capped USDC on Polygon against the signature. v1 trust asterisk: the pull is gated by the signature alone, so reimbursement carries a delivery-performance trust point on the operator. v2 will gate the pull on a Base → Polygon delivery proof (UMA optimistic or ZK).
create_order is a convenience wrapper that chains build → sign → submit in one call. build_order and submit_order are the lower-level primitives if you want to inspect or modify the typed-data before signing.

Catalog UUIDs are the shared address space

Catalog UUIDs are the most portable identifiers for hosted trading. PMXT’s catalog assigns a stable UUID to every prediction_markets.markets row and every prediction_markets.outcomes row, so those IDs are the right choice for Router results, cross-venue analytics, and records you want to survive venue API changes. Raw REST can use catalog UUIDs or an explicit venue + venue_outcome_id pair. Bare Polymarket conditionId / tokenId strings are not enough by themselves; include the venue tuple so the backend can reverse-resolve the catalog row. The catalog UUIDs are the same ones returned by the Router (/v0/markets, /v0/events). They are stable across venue API changes and survive venue re-listings.
The SDK accepts either form. Outcomes returned by pmxt.Polymarket(...).fetch_markets(...) carry a venue-native ID; outcomes returned by pmxt.Router(...) carry a catalog UUID. Pass either straight to create_order / build_order — the SDK forwards it as outcome_id (UUID) or (venue, venue_outcome_id) (venue-native) on the wire. Raw REST against trade.pmxt.dev follows the same shape. See Catalog UUID vs venue ID.

Custody: PreFundedEscrow

Non-custodial by construction — escrow contracts enforce that funds can only move against an EIP-712 signature from the user’s wallet (plus, for cross-chain buys, an oracle attestation). PMXT does not hold USDC in a hot wallet, an exchange-style omnibus account, or a multisig. The user’s wallet retains beneficial ownership at all times. USDC custody lives on Polygon in the PreFundedEscrow contract (chainId 137, sometimes called HomeEscrow). Outcome-token custody on non-Polygon venues lives on the venue’s native chain:
  • Polygon PreFundedEscrow holds the user’s USDC (USDC.e, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) and serves the payment legs for Polymarket, Opinion, and Limitless. Polymarket CTF tokens are custodied here too — same-chain.
  • BSC VenueEscrow (chainId 56) holds Opinion outcome tokens. For Opinion BUYs the operator deposits tokens here on the user’s behalf (no user signature on BSC). For Opinion SELLs the user signs a BSC-domain pull leg authorizing the operator to debit this escrow.
  • Base escrow holds Limitless outcome tokens. The operator delivers tokens here after buying on Limitless’ CLOB; the user funds and signs only on Polygon.
USDC enters via client.escrow.deposit_tx(amount) on Polygon, exits via client.escrow.withdraw_tx("request" | "claim" | "cancel", ...). Both build unsigned txs — your wallet signs and submits them. See Escrow Lifecycle.

Deployed contracts

Both contracts are non-upgradeable, non-pausable, with no Ownable role — only an immutable operator set at deploy time. Current operator addresses (immutable on the deployed contracts above):
  • Polygon PreFundedEscrow.operator: 0x84194eae9c63C1e3A769976bb762506d3443156d
  • BSC VenueEscrow.operator: 0x84194eae9c63C1e3A769976bb762506d3443156d
  • Polygon PreFundedEscrow.settlementOracle: 0x8aD93c0D15bC655b460dD700B1E25C86D1a728EE
The operator address is an EOA controlled by PMXT. Because operator is immutable on the deployed contracts, switching to a multisig would mean deploying new escrow contracts and migrating user balances — not an in-place rotation.

Trust model

There is no admin. The contracts have no Ownable, no Pausable, no upgrade proxy, no upgrade timelock, no fee setter. The privileged surface is the onlyOperator modifier in the shared OutcomeCustody base contract, and the operator address is immutable — set once in the constructor and unchangeable for the life of the contract. What this means concretely:
  • No one can pause withdrawals. Even if PMXT’s operator key is compromised, withdraw / claimWithdrawal keep working — unilateral withdrawal is always available, even if PMXT’s server is down.
  • No one can change the operator. A compromised PMXT cannot rotate to a new operator and skip user signatures.
  • No one can upgrade the contract. What’s deployed is what runs forever.
Every value-moving operator call requires either the user’s EIP-712 signature, the user as msg.sender, or (for cross-chain buys) an additional delivery attestation signed by an independent settlement oracle. The pmxt_api_key is a service-role credential: the key holder can read any associated wallet’s hosted data and forward signed orders, but the key alone cannot move funds. Keep pmxt_api_key on a server. Never ship it to a browser bundle. Never log it.

Audit status

Unaudited as of 2026-06-09. The contracts have not undergone a third-party security review. The contract is non-pausable and unilateral withdrawal is always available — if you want to exit, the timelock is your kill-switch and PMXT cannot block it. An audit report will be linked here when one is completed.

When to switch to self-hosted

Self-hosted runs pmxt-core on your own machine; the SDK talks to localhost. Both modes use the same SDK classes and method names where the capability is supported. The differences are where execution happens, who holds keys, and which write venues each mode supports. Choose self-hosted when you need sub-100ms latency, want to use raw venue credentials (Polymarket L2 API keys, Kalshi RSA, Smarkets sessions), have regulatory requirements that mandate direct counterparty status with the venue, or want venue-native trading where the venue exposes writes but hosted mode does not. See Self-hosted for setup.

What’s supported today

If a venue you need isn’t here and it exposes writes, run pmxt-core locally and pass raw venue credentials — see Self-hosted.

Next