Place Order
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
| Field | Type | Required | Description |
|---|---|---|---|
symbol | string | Yes | Market id, e.g. "SOL-USDC". |
side | string | Yes | "bid" (buy base) or "ask" (sell base). |
order_type | string | Yes | "limit", "ioc", or "fok". See Order Types. |
amount | integer | Yes | Order size in base units. |
price_limit | integer | Conditional | Worst acceptable price, in quote units per base. Required for a bid; an ask may use 0 to accept any clearing price. |
min_fill_size | integer | No | Reject fills smaller than this. Set equal to amount for all-or-none. Default 0 (any partial fill). See Execution Attributes. |
expiry_slot | integer | Yes | Solana slot past which the order auto-expires. Bounded by the market's max expiry. See Time in Force. |
order_id | string | Yes | A 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_nonce | integer | Yes | A per-order nonce bound into the signature. |
Collateral, opening, and proof
| Field | Type | Required | Description |
|---|---|---|---|
note_commitment | string | Yes | 32-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_amount | integer | No | The 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_commitment | string | Yes | 32-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_hash | string | Yes | 32-byte hex. The note's amount-independent inner hash (an opening field that anchors both the commitment and the nullifier). |
user_commitment | string | Yes | 32-byte hex. Binds the order's output notes to the correct owner on-chain. |
nullifier | string | Yes | 32-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_root | string | Yes | 32-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_proof | string | Yes | 256-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
| Field | Type | Required | Description |
|---|---|---|---|
anchors | array | Yes | Exactly 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)
| Field | Type | Required | Description |
|---|---|---|---|
viewing_pubkey | string | No | 32-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
| Field | Type | Required | Description |
|---|---|---|---|
trading_key | string | Yes | 32-byte hex. The Ed25519 public key that owns this order. |
trading_key_signature | string | Yes | 64-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.
| Field | Type | Description |
|---|---|---|
order_id | string | The order's id (the one you supplied). |
status | string | "accepted" means the order passed verification and entered the book. |
arrival_slot | integer | The slot the engine stamped on arrival; frozen for the order's life. |
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
| Status | Description |
|---|---|
pending | Accepted and resting in the book. |
matched | Matched in a batch; settling or settled on-chain. |
expired | Reached expiry_slot without (fully) filling. |
cancelled | Cancelled 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:
| Check | Status | Code |
|---|---|---|
Well-formed fields (hex widths, non-zero order_id) | 400 | 1001 |
| Hashed fields are canonical field elements | 400 | 1002 |
| Order amount meets the market minimum | 400 | 1004 |
| A bid has a positive price limit | 400 | 1005 |
The note opening re-derives the signed note_commitment | 400 | 1006 |
| The collateral covers the order's nominal cost plus its own fee | 400 | 1003 |
| The trading-key signature verifies over the canonical body | 403 | 1102 |
The order_id is not reused for a different order | 409 | 1201 |
| Per-account rate limit not exceeded | 429 | 1401 |
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.
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.