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.
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
-
Build. The SDK calls
POST /v0/trade/build-orderwith the outcome target, side, and amount. Catalog UUIDs go on the wire asoutcome_id; venue-native outcomes go on the wire asvenue+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 abuilt_order_idplus the payload to sign. -
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. -
Submit. The SDK calls
POST /v0/trade/submit-orderwith thebuilt_order_idand the signature. The server attaches the signature to the prepared order and submits to the venue. -
Settle. The venue matches the order. Settlement model varies by venue:
- Polymarket — same-chain on Polygon. The user signs one EIP-712
OrderParamsagainst the PolygonPreFundedEscrowdomain. 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
CrossChainOrderParamspayload on the PolygonPreFundedEscrowdomain, authorizing a capped spend of their USDC. The operator fronts BSC working capital, buys on Opinion’s CLOB, and callsdepositForUseron the BSCVenueEscrowto credit the user with the real outcome tokens. An independent settlement oracle observes the BSC delivery and signs aDeliveryAttestation. The operator submits the attestation tosettleCrossChainBuyon Polygon, which verifies the user signature, the attestation binding (order, user, token, destination escrow), the oracle signature, a non-zerotokensDelivered, 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 (
CrossChainSellPayParamson thePreFundedEscrowdomain) and a BSC pull leg (CrossChainSellPullParamson theVenueEscrowdomain). The operator fires both in parallel:settleCrossChainSellUSDCcredits the user’s USDC on Polygon,settleCrossChainSellTokenspulls 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).
- Polymarket — same-chain on Polygon. The user signs one EIP-712
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 everyprediction_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 thePreFundedEscrow contract (chainId 137, sometimes called HomeEscrow). Outcome-token custody on non-Polygon venues lives on the venue’s native chain:
- Polygon
PreFundedEscrowholds 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.
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 immutableoperator 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
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 noOwnable, 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/claimWithdrawalkeep 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.
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 runspmxt-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
- Trading quickstart — 60-second guided path.
- Escrow lifecycle — deposits, withdrawals, the timelock.
- Hosted errors — every error class and how to recover.

