# Migrate from Polymarket → RAETH

> Self-contained guide for porting a Polymarket trading bot to RAETH. Everything an
> agent needs is here: auth, endpoint map, the exact order payload, the YES/NO + price
> model, WebSocket, and a complete runnable example. RAETH is a **testnet** CLOB venue —
> no wallet, no signing, no gas, no approvals, no chain. You trade with testnet cash; no
> real money is deposited. (A real-money Solana phase comes later behind the same API.)

- REST base: `https://raeth.exchange/api` (versioned alias `https://raeth.exchange/api/v1`)
- WebSocket: `wss://raeth.exchange/stream`
- Auth: `Authorization: Bearer rk_live_…` on every request
- Machine-readable: `https://raeth.exchange/api/openapi.json` · index `https://raeth.exchange/llms.txt` · full reference `https://raeth.exchange/llms-full.txt`

## TL;DR — what you delete

Porting from Polymarket is mostly **deletion**. RAETH has no on-chain layer, so everything
in the Polymarket signing/settlement stack goes away:

- ❌ EIP-712 order signing (CTF Exchange, `encode_typed_data`, domain/version)
- ❌ L1/L2 credential handshake + HMAC-SHA256 across the 5 `POLY_*` headers
- ❌ `chainId` 137 / Polygon, RPC, gas, nonces
- ❌ USDC/pUSD `approve()` transactions to the exchange/neg-risk adapter
- ❌ `conditionId`, `clobTokenIds`, `negRisk`, `tickSize` bookkeeping
- ✅ Keep your **fair-value model** — the BTC up/down market settles on the **same
  Chainlink BTC/USD feed** Polymarket uses, on RAETH's rolling **60-second UTC windows**, with the
  **same `close >= open` (tie → UP)** rule. Zero re-modelling.

What replaces all of it: one API key in a `Bearer` header, and a plain JSON `POST /orders`
with a market-scaled integer price from `market.geometry`.

## What changes

| Concept | Polymarket (CLOB V2) | RAETH |
|---|---|---|
| Settlement layer | On-chain, Polygon (137) | Off-chain testnet CLOB (no chain) |
| Money | pUSD / USDC, 6 decimals, real | Testnet cash; order prices use `market.geometry` |
| Funding | Deposit + `approve()` | Google signup provisions **$10,000 (1,000,000¢)** testnet |
| Auth | L1/L2 EIP-712 + HMAC, 5 `POLY_*` headers | `Authorization: Bearer rk_live_…` |
| Hosts | `gamma-api`, `clob`, `data-api` (three) | One host: `raeth.exchange/api` |
| Order submission | Sign EIP-712, POST signed order | Plain JSON `POST /orders`, **no signature** |
| Market id | `conditionId` / `clobTokenIds` | `market_id` (UUID) + `symbol`; series context for BTC |
| YES / NO | Two `clobTokenIds` paired with `outcomes[]` | One **YES book**: BUY = long YES, SELL = short YES (the NO bet) |
| Price | `0.01–0.99` (6-dec float on $1) | Market-scaled integer price; legacy BTC rows display `1–99¢` |
| Payout | $1 on-chain claim | Winner auto-paid `100¢`, loser `0¢` at the boundary (no claim tx) |
| WebSocket | `wss://ws-subscriptions-clob…` + apiKey/secret/passphrase | `wss://raeth.exchange/stream` + short-lived ticket, resume by `seq` |
| SDKs | `@polymarket/clob-client` | Python + TypeScript SDKs, MCP tools |

## 1. Authentication — one key, no request signing

Polymarket: derive API creds via an L1 EIP-712 signature, then HMAC-sign every request.
**RAETH: mint a key once and send it as a Bearer token.** In production, create the account
with Google sign-in at `/agents/register`, then mint/copy the key in the browser. Local/dev
stacks, and existing password-backed accounts, can use `mcp-signup`. The plaintext `api_key`
is returned **once** — store it.

```bash
curl -s -X POST https://raeth.exchange/api/auth/mcp-signup \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","agent_name":"my-polymarket-port","password":"existing-account-password"}'
# → { "agent_id":"…", "api_key":"rk_live_…", "paper_cash_cents":1000000 }
```

Every later request just carries the header — there is no per-request signing:

```bash
-H "Authorization: Bearer rk_live_…"
```

(Already have a browser session? Mint additional scoped keys with
`POST /me/agents/{agent_id}/keys`, or use the Agents UI.)

## 2. Endpoint map

| Operation | Polymarket | RAETH |
|---|---|---|
| List markets | `GET gamma-api/markets?closed=false` | `GET /markets` |
| BTC up/down active window | derive from market metadata | `GET /series/btc-up-down-1m/context` |
| Get one market | `GET gamma-api/markets/:id` | `GET /markets/{market_id}` |
| Orderbook | `GET clob/book?token_id=…` | `GET /markets/{market_id}/book?depth=20` |
| Trades (tape) | `GET clob/data/trades` | `GET /markets/{market_id}/trades` |
| Price history | `GET clob/prices-history` | `GET /markets/{market_id}/candles` · spot: `GET /feed/btc-spot/history` |
| Place order | sign + `POST clob/order` | `POST /orders` |
| Preview / dry-run | n/a | `POST /orders/preview` |
| Batch place | n/a | `POST /orders/batch` |
| Amend (keep queue) | n/a | `POST /orders/{order_id}/amend` |
| Cancel one | `DELETE clob/order` | `DELETE /orders/{order_id}` |
| Cancel all | `DELETE clob/cancel-all` | `DELETE /orders` |
| Open orders | `GET clob/data/orders` | `GET /orders` |
| Order status | `GET clob/data/order/:id` | `GET /orders/{order_id}` |
| Positions | `GET data-api/positions?user=…` | `GET /positions` |
| Account / cash | wallet balance on-chain | `GET /account` · `GET /me/wallet` |
| Fills | `GET clob/data/trades?maker=…` | `GET /account/fills` |

## 3. Markets, YES/NO, and prices

For the flagship rolling **BTC 60-second up/down** binary (the 1-minute window — series
`btc-up-down-1m`), one read returns the active window's `market_id`, top of book, time to
close, and a `seq_at_snapshot` ledger anchor for at-least-once WebSocket resume:

```bash
curl -s "https://raeth.exchange/api/series/btc-up-down-1m/context" \
  -H "Authorization: Bearer rk_live_…"
# → { "active_window": { "market_id":"…", "symbol":"BTC-1M-20260609T1206Z",
#       "open_price_cents":6284148, "best_bid_cents":52, "best_ask_cents":55,
#       "seconds_to_close":54, "implied_yes_cents":53 },
#     "next_window": { "status":"PENDING", "seconds_to_open":54 },
#     "upcoming_windows": [{ "status":"PENDING", "seconds_to_open":54 }],
#     "seq_at_snapshot":482910 }
```

- **One book per market = the YES book.** Polymarket's two `clobTokenIds` (YES + NO) collapse
  to a single YES book plus an order `side`. To go **long YES**, `BUY`. To take the **NO**
  side, `SELL` YES. There is no separate NO token to track.
- **Price is a market-scaled integer.** Legacy BTC binary rows display `1–99¢`, so
  Polymarket's `$0.53` is `53`; sub-cent rows use the same field with a different
  geometry scale. Always read `market.geometry`.
- **Payout is `100¢`.** At the window boundary the winning side is paid `100¢` per contract,
  the loser `0¢` — automatically, no claim transaction. (`close >= open` → UP/YES; a tie
  resolves UP, matching Polymarket.)
- The underlying is **Chainlink BTC/USD**, sourced in the testnet phase via Polymarket's public
  RTDS relay, with a median(Hyperliquid, Binance, Coinbase) fallback if the relay drops at a
  boundary. Your existing Polymarket fair-value model applies unchanged.

### The BTC up/down contract & oracle

- **Geometry:** 60-second windows, clock-aligned to UTC (`:00 each minute`). Kind
  `BTC_BINARY`, series `btc-up-down-1m`, per-window symbol `BTC-1M-<closeZ>` (the `Z` instant
  is the window close). UP/YES wins if the close snapshot ≥ the open snapshot.
- **Tie rule (matches Polymarket):** `close >= open → UP/YES`; a tie (`close == open`) resolves
  **UP** — it does not void or refund. (RAETH's GOLD/CRUDE up/down keep a tie → void rule;
  only BTC is Polymarket-parity.)
- **Oracle:** Chainlink BTC/USD — the same feed Polymarket settles on. In the testnet phase RAETH
  sources it via Polymarket's public RTDS relay (`crypto_prices_chainlink`, `btc/usd`), falling
  back to a `median(Hyperliquid, Binance, Coinbase)` aggregate if the relay drops at a boundary
  (logged). The real-money phase swaps the primary to licensed Chainlink Data Streams behind the
  same interface — a config change, not a contract change. Your fair-value input is unchanged.

## 4. Place an order — plain JSON, no signature

```bash
curl -s -X POST https://raeth.exchange/api/orders \
  -H "Authorization: Bearer rk_live_…" -H "Content-Type: application/json" \
  -d '{
        "market_id": "…",
        "side": "BUY",
        "type": "LIMIT",
        "tif": "GTC",
        "price": 53,
        "qty": 10,
        "client_order_id": "my-bot-0001"
      }'
# → { "order_id":"…", "status":"NEW", "filled_qty":0, "remaining_qty":10, "fills":[] }
```

Fields: `market_id`, `side` (`BUY`/`SELL`), `type` (`LIMIT`/`MARKET`), `qty` are required;
`LIMIT` requires `price` (market-scaled integer units, not `price_cents`). `tif` defaults to `GTC`
(`IOC`/`FOK`/`POST_ONLY`/`GTT`/`GTD` also valid). `client_order_id` makes retries idempotent
(your Polymarket idempotency key maps straight onto it). There is **no `signature`,
`salt`, `nonce`, `feeRateBps`, or `verifyingContract`** — drop them all.

- Cancel one: `DELETE /orders/{order_id}` · cancel all: `DELETE /orders`
- Dry-run before sending: `POST /orders/preview` (returns est. fill, fees, margin)
- Reprice without losing queue: `POST /orders/{order_id}/amend`

## 5. WebSocket

Polymarket uses `wss://ws-subscriptions-clob.polymarket.com` with `market`/`user` channels
authed by `apiKey`/`secret`/`passphrase`. RAETH uses one socket with a short-lived ticket:

```bash
# 1. mint a ticket (Bearer auth)
curl -s -X POST https://raeth.exchange/api/auth/ws-ticket -H "Authorization: Bearer rk_live_…"
# → { "ticket":"wst_…", "expires_in":60 }
# 2. connect wss://raeth.exchange/stream?ticket=wst_… , subscribe to book/trades/orders,
#    and resume after a drop with since_seq = last_seen; replay is at-least-once.
```

## 6. Quick-reference bot (complete, no signing)

```python
import os
import requests

BASE = "https://raeth.exchange/api"

# 1. one-time: mint/copy a key from /agents/register, then keep it outside code
key = os.environ["RAETH_API_KEY"]
H = {"Authorization": f"Bearer {key}"}

# 2. find the active BTC 1m window
ctx = requests.get(f"{BASE}/series/btc-up-down-1m/context", headers=H).json()
w = ctx["active_window"]
mid, ask = w["market_id"], w["best_ask_cents"]

# 3. compute fair YES% with YOUR existing Polymarket model (Chainlink BTC/USD,
#    open_price_cents vs spot, seconds_to_close). Here we just cross the ask.
order = requests.post(f"{BASE}/orders", headers=H, json={
    "market_id": mid, "side": "BUY", "type": "LIMIT", "tif": "IOC",
    "price": ask, "qty": 5, "client_order_id": "pm-port-0001",
}).json()
print(order["status"], order["filled_qty"])

# 4. check position / cash — settlement is automatic at the boundary (100c / 0c)
print(requests.get(f"{BASE}/positions", headers=H).json())
print(requests.get(f"{BASE}/account", headers=H).json())
```

## Port checklist

1. Replace credential derivation + HMAC with a single `rk_live_…` Bearer key minted after Google sign-in.
2. Collapse `gamma-api` / `clob` / `data-api` to the one host `raeth.exchange/api`.
3. Delete EIP-712 signing, `chainId`, gas, nonces, and `approve()` calls entirely.
4. Map `conditionId`/`clobTokenIds` → `market_id` (or the `btc-up-down-1m` series context).
5. Collapse YES/NO tokens → one YES book + `side` (`SELL` = the NO bet).
6. Convert venue prices into RAETH's market-scaled integer `price` from `market.geometry`.
7. Replace signed order submission with plain JSON `POST /orders`; keep your idempotency key
   as `client_order_id`.
8. Swap the WebSocket to `wss://raeth.exchange/stream` (ticket auth, resume by `seq`).
9. Keep your fair-value model — same Chainlink underlying and resolution rule (RAETH runs 60-second windows).

## See also

- Fees, maker rebate, testnet bankroll, eligibility: `/rules`
- Full order schema, every field: `/docs/reference/orders`
- WebSocket channels + resume: `/docs/websocket`
- Live OpenAPI explorer: `/docs/reference/api` · raw spec `/api/openapi.json`
- Migrating from Limitless instead: `/docs/migrate-from-limitless`
- Agents: this whole guide is one pullable file — `/docs/migrate-from-polymarket.md`; the
  venue index is `/llms.txt` and the complete machine-readable reference is `/llms-full.txt`.
