Skip to main content

Place Order

TL;DR

POST /orders submits a new order. The body carries the usual economic fields (symbol, side, type, amount, price) plus the cryptographic backing that makes the order private and trustless: the collateral-note commitment, a zero-knowledge input proof, an owner-commitment opening, a continuation anchor pool, and a trading-key signature over the whole canonical body. The SDK builds and signs all of this from your keys and a deposited note.

POST /orders

Auth: Authorization: Bearer <token> and a trading-key signature in the body.

How a Darknyx order differs

On a transparent venue, placing an order is just sending its economic fields. On Darknyx an order is fully collateralized by a specific note you already deposited, and it is private, so the request also carries:

  • the commitment of the collateral note, and a secret opening of that note the in-enclave prover needs;
  • a zero-knowledge input proof that the note exists in the on-chain tree and is yours to spend;
  • a continuation anchor pool that lets the engine settle partial fills and keep the remainder working without a round-trip to you per fill;
  • an Ed25519 signature from your trading key over the canonical body, so the engine can attribute, and ultimately settle, the order to you without any per-order on-chain transaction;
  • (optional) a viewing key the engine uses to store each change amount encrypted on-chain, so a partial fill's change note stays recoverable on any device and after an engine redeploy.

You do not assemble these by hand. The SDK takes your keys and a spendable note and produces a ready-to-sign request. The full field reference is here so the wire contract is unambiguous.

Request body

Economic fields

FieldTypeRequiredDescription
symbolstringYesMarket id, e.g. "SOL-USDC".
sidestringYes"bid" (buy base) or "ask" (sell base).
order_typestringYes"limit", "ioc", or "fok". See Order Types.
amountintegerYesOrder size in base units.
price_limitintegerConditionalWorst acceptable price, in quote units per base. Required for a bid; an ask may use 0 to accept any clearing price.
min_fill_sizeintegerNoReject fills smaller than this. Set equal to amount for all-or-none. Default 0 (any partial fill). See Execution Attributes.
expiry_slotintegerYesSolana slot past which the order auto-expires. Bounded by the market's max expiry. See Time in Force.
order_idstringYesA client-chosen 16-byte id, hex. Must be unique and non-zero. The SDK can derive ids deterministically from your seed (deriveOrderId), so you can reconcile or recover your order set on a fresh device.
arrival_nonceintegerYesA per-order nonce bound into the signature.

Collateral, opening, and proof

FieldTypeRequiredDescription
note_commitmentstringYes32-byte hex. The commitment of the collateral note backing this order. The note must exist in the tree and be lockable (not already locked).
collateral_amountintegerNoThe value the collateral note actually carries, when it exceeds the order's nominal cost. Lets you point a large note at a small order and receive the surplus back as a change note. Omit for exact collateral.
owner_commitmentstringYes32-byte hex. The collateral note's owner commitment, part of the secret opening the in-enclave prover re-derives the commitment from. Distinct from user_commitment. Held in enclave memory only.
note_inner_hashstringYes32-byte hex. The note's amount-independent inner hash (an opening field that anchors both the commitment and the nullifier).
user_commitmentstringYes32-byte hex. Binds the order's output notes to the correct owner on-chain.
nullifierstringYes32-byte hex. Precomputed client-side (it needs the spending key, which never enters the enclave). Opaque to the engine; carried into the settlement payload.
merkle_rootstringYes32-byte hex. The tree root the input proof was generated against. Must still be in the on-chain root window at settlement time.
valid_input_proofstringYes256-byte hex. The zero-knowledge proof that the collateral note is in the tree and spendable. The engine relays it unverified; the on-chain program verifies it at lock time.

Continuation anchor pool

FieldTypeRequiredDescription
anchorsarrayYesExactly 10 continuation anchors. Each is an { inner_hash, nullifier } pair (both 32-byte hex) for a future change note, so the engine can settle a partial fill and re-lock the remainder without asking you for a new note per fill. The hash of the pool is bound into the signature. See The Anchor Pool.

Change-amount recovery (optional)

FieldTypeRequiredDescription
viewing_pubkeystringNo32-byte hex. An X25519 viewing-encryption public key the SDK derives from your seed. When present, the engine encrypts each of this order's change amounts to it and stores the ciphertext on-chain at settlement, so a change note stays recoverable permanently, even after an engine redeploy clears the live fill memo. It is not part of the signed canonical body: a wrong key only affects your own recoverability, and the ciphertext is self-verifying when you decrypt it. The SDK populates it by default; omit it to rely only on the live Fills Channel memo. See Recovering your notes.

Signature

FieldTypeRequiredDescription
trading_keystringYes32-byte hex. The Ed25519 public key that owns this order.
trading_key_signaturestringYes64-byte hex. Signature over the canonical encoding of the body: every economic field plus the anchor-pool hash and arrival_nonce.

Example

# In practice the SDK produces order.json from your keys + a spendable note.
curl -s -X POST "$GATEWAY/orders" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"symbol": "SOL-USDC",
"side": "bid",
"order_type": "limit",
"amount": 10000000,
"price_limit": 150000000,
"min_fill_size": 0,
"expiry_slot": 309490000,
"order_id": "aa00000000000000000000000000000001",
"note_commitment": "…",
"user_commitment": "…",
"arrival_nonce": 1,
"trading_key": "…",
"trading_key_signature": "…",
"owner_commitment": "…",
"note_inner_hash": "…",
"nullifier": "…",
"merkle_root": "…",
"valid_input_proof": "…",
"viewing_pubkey": "…",
"anchors": [ { "inner_hash": "…", "nullifier": "…" }, "… 10 total …" ]
}'

Success response

{
"order_id": "aa00000000000000000000000000000001",
"status": "accepted",
"arrival_slot": 309482113
}

Returned with 202 Accepted.

FieldTypeDescription
order_idstringThe order's id (the one you supplied).
statusstring"accepted" means the order passed verification and entered the book.
arrival_slotintegerThe slot the engine stamped on arrival; frozen for the order's life.
Accepted is not filled

A 202 means the order passed signature and collateral verification and entered the book, not that it has filled. Track fills via GET /orders/{order_id} or the Orders Channel.

Order status lifecycle

StatusDescription
pendingAccepted and resting in the book.
matchedMatched in a batch; settling or settled on-chain.
expiredReached expiry_slot without (fully) filling.
cancelledCancelled by you, by a modify, or on session disconnect.

A market or fill-or-kill order that cannot execute in its arrival batch leaves the book immediately rather than resting.

Idempotency

order_id is your idempotency key. A retry that re-sends the byte-identical signed order returns the original acceptance (202, same order_id) instead of a conflict, so a network blip is safe to retry. Reusing an order_id for a different order (any economic field changed) is a real conflict and returns 409 (code 1201). Because order ids are client-chosen, pick a fresh one per distinct order.

Verification at intake

Every order is verified before it enters the book. A non-202 response carries a structured error code; the conditions:

CheckStatusCode
Well-formed fields (hex widths, non-zero order_id)4001001
Hashed fields are canonical field elements4001002
Order amount meets the market minimum4001004
A bid has a positive price limit4001005
The note opening re-derives the signed note_commitment4001006
The collateral covers the order's nominal cost plus its own fee4001003
The trading-key signature verifies over the canonical body4031102
The order_id is not reused for a different order4091201
Per-account rate limit not exceeded4291401

Because the opening is checked against the signed commitment, the secret opening fields are cryptographically pinned to your signature without being part of the signed canonical body.

Rate limits are weighted

Order management is rate-limited per account with a token bucket; cancels are cheap, place and modify cost more. A 429 includes a Retry-After header. For high-frequency management prefer the WebSocket trading socket.