Skip to content

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.

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.
  • curl and jq.

The order manager listens on CLOB_HTTP_ADDR (default 127.0.0.1:8080). Only GET /health is public; everything else needs authentication.

Terminal window
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.

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:

Terminal window
# From a keypair file
export CLOB_TOKEN=$(cargo run -p get-token --quiet -- --keypair ~/.config/solana/id.json)
# Or from a BIP39 seed phrase
export CLOB_SEED_PHRASE="word1 word2 … word12"
export CLOB_TOKEN=$(cargo run -p get-token --quiet)

Send it as a bearer token on every request:

Terminal window
curl -s http://127.0.0.1:8080/v1/markets \
-H "Authorization: Bearer $CLOB_TOKEN" | jq

The 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.

Terminal window
curl -s http://127.0.0.1:8080/v1/markets \
-H "Authorization: Bearer $CLOB_TOKEN" | jq

Each 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.

Terminal window
CONDITION_ID=0xdemo-condition
TOKEN_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…"
}

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:

FieldMust be
takerthe zero address — orders are public, not bilateral
nonce"0" — there is no on-chain nonce account
signatureType0 (EOA); the program settles EOA orders only
signerequal 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=0

The sign-order utility does this for you:

Terminal window
cat order.json | cargo run -p sign-order --quiet -- --wallet alice
# → 64-byte ed25519 signature, hex

Put 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.

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.

Terminal window
cargo run -p delegate --quiet -- set --keypair ~/.config/solana/id.json --amount 1000000000
cargo run -p delegate --quiet -- show --keypair ~/.config/solana/id.json

This is what lets you go offline after posting an order — you never co-sign the settlement transaction. Revoke with delegate revoke.

Terminal window
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:

statusMeans
liveRested on the book, nothing crossed
matchedCrossed and executed; transactionsHashes has the settlements
delayedHeld by the pending service (game-start window)
unmatchedCouldn’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).

Terminal window
# 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 history
curl -s "http://127.0.0.1:8080/v1/trades?market=$CONDITION_ID" \
-H "Authorization: Bearer $CLOB_TOKEN" | jq

A 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.

Terminal window
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.