Skip to main content
This page takes the private key of your key’s associatedWallet from nothing to fully trading in five steps. Every code block is real ethers v6 against the live testnet — paste it in, swap your API key + private key, run.
Don’t have an API key yet? Keys are issued by PredictStreet ops — they are never self-service. Email your integration manager (or partners@predictstreet.com) with the four items in API keys → Getting a key and you’ll receive a testnet ps_live_… key plus the IP-allowlist + KYC prerequisites for orders:write and vault:write scopes.

Prerequisites

1. Mint testnet USDC to your EOA

MockCollateral exposes a public mint(address, uint256) on testnet — mint as much as you want. Note: USDC needs to land in your EOA first; the vault deposit moves it from EOA → vault in a later step.
You also need a small ADI balance for gas. On testnet grab it from the public faucet — ~5 ADI to your EOA covers deploy + deposit + a handful of trades.

2. Get your vault address

The platform deploys your vault automatically the moment your associated wallet clears KYC tier 1 — you do not call VaultFactory.deployVault yourself, and you do not pay the deploy gas. KYC clearance is a hard prerequisite for any orders:write / vault:write key, so by the time you have a working API key, your vault is either deployed or about to be. Poll GET /api/me/vault until deployed: true:
Save vault. It’s the address you’ll send as maker in every signed order, and the verifyingContract in vault-side EIP-712 domains (splits / withdrawals).
Vault deploy isn’t currently surfaced on the typed WS channels — poll GET /api/me/vault until deployed: true like the snippet above. Auto-deploy normally completes within a few seconds.

3. Approve + deposit USDC into the vault

After ~1 block the platform reflects the deposit and your /api/me/balances will show available = 500 USDC. The on-chain approve step stays on your side regardless of auto-deploy — ERC-20 transferability is owner-only by design. See Deposits for the full lifecycle and Deposit limits for the daily / weekly / monthly caps applied to your vault.
Per-EOA deposit limits are initialised by the platform automatically alongside the vault deploy. By the time GET /api/me/vault returns deployed: true, the limits are already registered and the deposit call succeeds.

4. Place orders

EIP-712 helper

Every order is signed against the on-chain CTFExchange Order struct (11 fields). See EIP-712 signing for the full type table. Helper:
Binary vs neg-risk domain. The verifyingContract you sign against depends on the market: 2-outcome binary markets settle on CTFExchange, 3+-outcome (neg-risk) markets settle on PredictStreetNegRiskCtfExchange. The market response carries negRiskEligible: boolean — branch on it before signing or you’ll get bad_signature rejections at order placement:
The Order struct itself is identical between the two — only the domain verifyingContract differs.

Pull a real tokenId (and the per-market fee)

LIMIT BUY (rests in the book)

HTTP 201 does not mean “order accepted by the matcher.” The endpoint returns 201 Created as soon as core-api validates the request envelope and forwards it — even when the matcher later rejects it. Always branch on the body’s status field, not the HTTP code:

MARKET BUY (best-effort, IOC)

MARKET requires timeInForce: ioc or fok. The price field still travels (it acts as a slippage cap — you only fill at price-or-better).

Cancelling

5. Split USDC → YES + NO so you can SELL

A SELL order must reference outcome ERC-1155 tokens that already sit in your vault. You get them by depositing USDC and then calling vault.splitPosition(...) with a backend co-signature. The backend returns the co-sig from POST /api/vault/split-signature; you sign the same struct yourself, then submit on-chain.
After this confirms the vault holds 50 YES + 50 NO ERC-1155. Now SELL of either side works exactly like BUY but with side: 'sell':

Order types — what you can ask for

The platform supports the following combinations end-to-end: price is always required, even for MARKET — it acts as the slippage cap. The matcher will only consume liquidity at price or better. expiry (= EIP-712 expiration) defaults to 0 (no on-chain expiry). A non-zero value is fine for off-chain matching but races async on-chain settlement; if the deadline passes between match and submit, the on-chain leg reverts with OrderExpired(). Recommend keeping 0 unless you specifically need a hard TTL well above worst-case settlement latency.

Verifying state after each step

Common pitfalls

Where to go next

EIP-712 signing reference

Full struct + Python / Go / Rust signing examples.

Order lifecycle

PENDING → OPEN → PARTIAL → FILLED state machine + on-chain settlement.

Vault contract reference

Full ABI, dual-sig flow, emergency withdraw, digest invalidation.

Time-in-force semantics

GTC / IOC / FOK behaviour in detail.