Place Your First Order
End to end: authenticate, pick a market, sign an order, post it, and read back the fill. Everything here runs against the Order Manager’s HTTP API.
Before you start
Section titled “Before you start”You need:
- A Solana wallet (keypair file or 12-word seed phrase) funded with USDC on the network you’re targeting.
- A delegate allowance granted to the program, so your tokens can settle without you co-signing. See step 5.
curlandjq.
The order manager listens on CLOB_HTTP_ADDR (default 127.0.0.1:8080).
Only GET /health is public; everything else needs authentication.
1. Check the service is up
Section titled “1. Check the service is up”curl -s http://127.0.0.1:8080/health | jq{ "ready": true }ready: false means startup hasn’t finished building the order books from
the markets catalogue. Wait and retry.
2. Authenticate (SIWS)
Section titled “2. Authenticate (SIWS)”GoMarket uses Sign-In with Solana. Your wallet signs the literal string
Sign in with <aud> — where aud is the API client id, GoMarket API by
default — and you send an unsecured JWT carrying that signature.
The get-token utility in the CLOB repo builds one:
# From a keypair fileexport CLOB_TOKEN=$(cargo run -p get-token --quiet -- --keypair ~/.config/solana/id.json)
# Or from a BIP39 seed phraseexport CLOB_SEED_PHRASE="word1 word2 … word12"export CLOB_TOKEN=$(cargo run -p get-token --quiet)Send it as a bearer token on every request:
curl -s http://127.0.0.1:8080/v1/markets \ -H "Authorization: Bearer $CLOB_TOKEN" | jqThe sub claim of that token is your authenticated address. The server
uses it as the owner of every order you insert and ignores any owner field
you send in the body. Tokens expire after CLOB_SIWS_MAX_AGE_SECONDS
(default 300s), so mint a fresh one per session.
Full details in Authentication.
3. Find a market and its token IDs
Section titled “3. Find a market and its token IDs”curl -s http://127.0.0.1:8080/v1/markets \ -H "Authorization: Bearer $CLOB_TOKEN" | jqEach market is identified by a condition ID, and each of its two sides by a token ID (also called the asset ID). You trade a token ID, not a condition ID.
CONDITION_ID=0xdemo-conditionTOKEN_ID=100 # the YES side
curl -s "http://127.0.0.1:8080/v1/order-book-summary?condition_id=$CONDITION_ID&token_id=$TOKEN_ID" \ -H "Authorization: Bearer $CLOB_TOKEN" | jq{ "market": "0xdemo-condition", "asset_id": "100", "bids": [{ "price": "0.48", "size": "1200" }], "asks": [{ "price": "0.52", "size": "900" }], "hash": "9f2c…"}4. Build and sign the order
Section titled “4. Build and sign the order”An order expresses “I give up maker_amount of one asset to receive
taker_amount of the other.” For a BUY, you give collateral and receive
outcome tokens; for a SELL, the reverse. Both amounts are raw token units
— USDC and outcome tokens both use 6 decimals, so 50000 is 0.05.
To buy 100 YES shares at $0.50 you offer 50 USDC for 100 shares:
{ "salt": 1234567890, "maker": "<your base58 address>", "signer": "<your base58 address>", "taker": "11111111111111111111111111111111", "tokenId": "100", "makerAmount": "50000000", "takerAmount": "100000000", "side": "BUY", "expiration": "0", "nonce": "0", "feeRateBps": "0", "signatureType": 0, "signature": ""}Constraints the validator enforces on insert:
| Field | Must be |
|---|---|
taker | the zero address — orders are public, not bilateral |
nonce | "0" — there is no on-chain nonce account |
signatureType | 0 (EOA); the program settles EOA orders only |
signer | equal to maker |
expiration | "0" for GTC/FOK, a future unix second for GTD |
Sign the order hash — a domain-separated SHA-256 over the fields, with the signature itself excluded:
sha256( b"CTF_EXCHANGE_ORDER_V1" ‖ salt:u64LE ‖ maker:32B ‖ signer:32B ‖ taker:32B ‖ token_id_len:u64LE ‖ token_id:UTF8 ‖ maker_amount:u64LE ‖ taker_amount:u64LE ‖ expiration:u64LE ‖ nonce:u64LE ‖ fee_rate_bps:u16LE ‖ side:u8 ‖ signature_type:u8 )
side: BUY=0, SELL=1 signature_type: EOA=0The sign-order utility does this for you:
cat order.json | cargo run -p sign-order --quiet -- --wallet alice# → 64-byte ed25519 signature, hexPut that hex string in the signature field. The same hash is recomputed
on-chain at settlement and verified against a top-level ed25519 instruction,
so a wrong hash fails late and loudly.
5. Grant the delegate allowance
Section titled “5. Grant the delegate allowance”Your maker tokens move at settlement through a program delegate PDA under
a standing SPL Approve allowance. Without it, matches involving your order
fail on-chain.
cargo run -p delegate --quiet -- set --keypair ~/.config/solana/id.json --amount 1000000000cargo run -p delegate --quiet -- show --keypair ~/.config/solana/id.jsonThis is what lets you go offline after posting an order — you never co-sign
the settlement transaction. Revoke with delegate revoke.
6. Post the order
Section titled “6. Post the order”curl -s -X POST http://127.0.0.1:8080/v1/orders \ -H "Authorization: Bearer $CLOB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "order": { … the signed order above … }, "owner": "<your address>", "orderType": "GTC" }' | jq{ "success": true, "errorMsg": "", "orderID": "0x7f3a…", "transactionsHashes": ["5Kx…"], "status": "matched"}success: true doesn’t mean you got filled — read status:
status | Means |
|---|---|
live | Rested on the book, nothing crossed |
matched | Crossed and executed; transactionsHashes has the settlements |
delayed | Held by the pending service (game-start window) |
unmatched | Couldn’t fill — a FOK that found no liquidity |
Order types: GTC (rest until cancelled), GTD (rest until expiration),
FOK (fill completely right now or die).
7. Read back your orders and fills
Section titled “7. Read back your orders and fills”# Resting orders (always scoped to your authenticated address)curl -s http://127.0.0.1:8080/v1/open-orders \ -H "Authorization: Bearer $CLOB_TOKEN" | jq
# Trade historycurl -s "http://127.0.0.1:8080/v1/trades?market=$CONDITION_ID" \ -H "Authorization: Bearer $CLOB_TOKEN" | jqA trade moves through MINED → CONFIRMED as the executor’s confirmation
worker follows it to Solana’s finalized commitment. A trade that fails
on-chain lands as FAILED, and the order manager ejects the affected resting
orders automatically.
8. Cancel
Section titled “8. Cancel”curl -s -X POST http://127.0.0.1:8080/v1/orders/cancel \ -H "Authorization: Bearer $CLOB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"orderID": "0x7f3a…"}' | jq{ "canceled": ["0x7f3a…"], "not_canceled": {} }Anything that couldn’t be cancelled comes back in not_canceled with a
reason. There’s also /v1/orders/cancel-batch, /cancel-all and
/cancel-market. See Manage Orders.
- Match Types — why your BUY can fill against another BUY
- Fees — what a taker actually pays
- Order Manager API — every endpoint and field
- GoCombo parlays — multi-leg positions