> ## Documentation Index
> Fetch the complete documentation index at: https://docs.noise.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Orders

> Sign, submit, and cancel orders.

Orders are **signed client-side**. The client builds an EIP-712 `Order` struct, signs it with the trading wallet, and submits the signed values. The server recovers the signer, recomputes the order hash, enforces the market's trading rules, and hands the order to the matching engine.

The API signs nothing on a trader's behalf, and no endpoint can amend a signed order, because the flags and prices are inside the signature.

## Before signing

Read `GET /assets` for the target market. The signature covers `marketId` while the request body carries `assetId`, so both are required:

```json theme={null}
{
  "assets": [
    {
      "assetId": "3f1c…-uuid",
      "ticker": "EXAMPLE",
      "name": "Example Trend",
      "status": "LISTED",
      "tickerState": "CONTINUOUS",
      "marketId": 42,
      "tickSize": "0.001",
      "lotSize": "0.01",
      "maxLeverage": "1"
    }
  ]
}
```

`marketId` is nullable. A market that has never published a snapshot reports `null`, and no valid order can be signed for it, because the signature covers that field.

`tickSize`, `lotSize`, and `maxLeverage` are also nullable, and here `null` means the market publishes no such constraint rather than that the market is unusable. Where a value is present, prices must be a multiple of `tickSize`, sizes a multiple of `lotSize`, and leverage within `maxLeverage`; violations are rejected as `TICK_SIZE`, `LOT_SIZE`, or `MAX_LEVERAGE`. Read the fields per market rather than assuming a constraint exists.

A universal **0.000001** precision floor applies regardless.

## The Order struct

The **domain**, from `GET /status`, using `exchange.chainId` and `exchange.clearingHouseAddress`:

```json theme={null}
{ "name": "NoiseExchange", "version": "1", "chainId": 8453, "verifyingContract": "0x..." }
```

**Fields**, in this order:

| Field         | Type      | Notes                                                         |
| ------------- | --------- | ------------------------------------------------------------- |
| `trader`      | `address` | The trading wallet                                            |
| `marketId`    | `uint32`  | From `GET /assets`                                            |
| `nonce`       | `uint64`  | Unique per wallet. Engine order identity is `(trader, nonce)` |
| `expiry`      | `uint64`  | Unix seconds, at most 2 years out                             |
| `quantity`    | `uint128` | Size, scaled by 1e6                                           |
| `limitPrice`  | `uint128` | Price, scaled by 1e6                                          |
| `minFillSize` | `uint128` | Always `0`                                                    |
| `collateral`  | `uint128` | Collateral committed, scaled by 1e6                           |
| `isLong`      | `bool`    |                                                               |
| `reduceOnly`  | `bool`    |                                                               |
| `postOnly`    | `bool`    |                                                               |
| `orderType`   | `uint8`   | `LIMIT` = 0, `MARKET` = 1                                     |

<Warning>
  **Exactly one of `postOnly` or `reduceOnly` must be `true`.**

  Settlement validates both sides of a match and rejects an order whose flags are equal, so an
  order with neither set can rest on the book but can never fill. Submitting one is rejected as
  `INVALID_FLAGS`. Because the flags are inside the signed struct, the server cannot correct
  them.
</Warning>

The four monetary fields are signed as **1e6 fixed-point integers** but submitted as **decimal strings**. Scale for the signature, then send the human-readable form.

## Submitting

```ts theme={null}
import { parseUnits } from "viem";

const asset = assets.find((a) => a.ticker === "EXAMPLE");
const size = "10";
const price = "1.234";
const collateral = "12.34";
const nonce = Date.now();                            // any unused uint64
const expiry = Math.floor(Date.now() / 1000) + 3600;

const signature = await account.signTypedData({
  domain: {
    name: "NoiseExchange",
    version: "1",
    chainId: status.exchange.chainId,
    verifyingContract: status.exchange.clearingHouseAddress as `0x${string}`,
  },
  types: {
    Order: [
      { name: "trader", type: "address" },
      { name: "marketId", type: "uint32" },
      { name: "nonce", type: "uint64" },
      { name: "expiry", type: "uint64" },
      { name: "quantity", type: "uint128" },
      { name: "limitPrice", type: "uint128" },
      { name: "minFillSize", type: "uint128" },
      { name: "collateral", type: "uint128" },
      { name: "isLong", type: "bool" },
      { name: "reduceOnly", type: "bool" },
      { name: "postOnly", type: "bool" },
      { name: "orderType", type: "uint8" },
    ],
  },
  primaryType: "Order",
  message: {
    trader: account.address,
    marketId: asset.marketId,
    nonce: BigInt(nonce),
    expiry: BigInt(expiry),
    quantity: parseUnits(size, 6),
    limitPrice: parseUnits(price, 6),
    minFillSize: 0n,
    collateral: parseUnits(collateral, 6),
    isLong: true,
    reduceOnly: false,
    postOnly: true,
    orderType: 0,
  },
});

const res = await call("POST", "/api/trader/v1/orders", {
  assetId: asset.assetId,
  side: "LONG",
  type: "LIMIT",
  size,
  price,
  collateral,
  postOnly: true,
  reduceOnly: false,
  nonce,
  expiry,
  signature,
});
```

The submitted fields must agree with the signed values, or the recovered hash will not match and the order is rejected as `INVALID_SIGNATURE`.

Response:

```json theme={null}
{ "orderHash": "0x...", "idempotent": false }
```

**Idempotency** is by order hash: resubmitting an identical signed order returns the original with `idempotent: true`. There is no client order id, but the order hash can be computed locally before submitting, so the identifier is known in advance.

### Order types

A **limit** order executes at its price or better, and rests on the book until it fills, expires, or is cancelled.

A **market** order still carries a `limitPrice`, which acts as a **slippage bound**, because it is submitted as an aggressive limit order and so cannot execute at an unbounded price. The matching engine rejects a market order into an empty book, so limit orders are the safer choice for quoting rather than taking.

### Batching

`POST /orders/batch` takes up to 20 orders and `POST /orders/batch-cancel` up to 50 hashes. Both return per-item results rather than failing as a unit:

```json theme={null}
{ "results": [{ "success": true, "orderHash": "0x...", "error": null }] }
```

Always check each entry, because a partial success is normal.

## Accepted is not filled

A `200` means the engine took the order. **It can still reject it afterwards**, and that surfaces on the order rather than on the submit response.

Poll `GET /account/orders/{orderHash}` or watch the `orders` [stream](/trader-api/streams). A rejected or cancelled order carries `rejectCode`, a small integer from the engine, and `rejectReason`, its decoded description. Both are `null` while the order is live.

<Warning>
  `rejectCode` is decoded against **two different maps**, selected by the order's `status`. The same
  integer means different things in each: code 2 is a rate limit on a rejected order but an expiry
  on a cancelled one. Read `status` first, then decode. An unrecognised code carries a null
  `rejectReason`.
</Warning>

For a **rejected** order, branch on `rejectCode` and treat `rejectReason` as display text. These are the codes a trading client meets most often; the engine's map is larger:

| Code | Meaning                                                                                                                                                                                                                           |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1    | Self-trade prevention                                                                                                                                                                                                             |
| 2    | Rate limited                                                                                                                                                                                                                      |
| 3    | Insufficient margin. The account needs funding                                                                                                                                                                                    |
| 4    | Invalid price                                                                                                                                                                                                                     |
| 5    | Invalid quantity                                                                                                                                                                                                                  |
| 8    | Invalid open/close direction                                                                                                                                                                                                      |
| 9    | Wallet rejection, raised when the exchange cannot draw against the wallet on-chain. A missing collateral approval is the usual cause, so check that first ([Getting Tradeable](/trader-api/getting-tradeable#approve-collateral)) |
| 16   | Wallet not registered                                                                                                                                                                                                             |
| 19   | Ticker paused                                                                                                                                                                                                                     |

For a **cancelled** order the codes are unrelated to the table above:

| Code | Meaning                           |
| ---- | --------------------------------- |
| 0, 9 | Cancelled                         |
| 1    | Cancelled manually                |
| 2    | Order expired                     |
| 3    | Cancelled due to price adjustment |
| 4    | Cancelled (self-hedge)            |
| 10   | Cancel reference invalid          |

## Cancelling

| Route                             | Effect                                                |
| --------------------------------- | ----------------------------------------------------- |
| `POST /orders/{orderHash}/cancel` | Cancel one order                                      |
| `POST /orders/batch-cancel`       | Cancel up to 50 by hash                               |
| `POST /orders/cancel-all`         | Cancel everything, returns `requested` and `accepted` |

### Dead man's switch

`POST /orders/cancel-all-after` arms a countdown. If it lapses, every open order for the key is cancelled, so a bot that crashes or loses connectivity does not leave exposure resting on the book.

```ts theme={null}
await call("POST", "/api/trader/v1/orders/cancel-all-after", { timeout: 60 });
```

Refresh it every 15 to 20 seconds while the strategy is running. Send `{ "timeout": 0 }` to disarm.

<Info>
  Treat this as mandatory for any unattended strategy. It is the only mechanism that withdraws
  resting orders when the client process itself is what failed.
</Info>

## When trading is halted

While trading is paused, order submission returns `503 TRADING_DISABLED` and the `system:status` stream fires.

**Cancels remain accepted**, deliberately, so exposure can always be pulled during a halt. Error handling should let a halt stop submissions without also blocking cancels.

`GET /assets` also reports a `tickerState` per market: `AUCTION` means orders accrue without continuous matching, `CONTINUOUS` is normal matching, `DELISTED` markets stay listed so old positions still resolve, and `UNKNOWN` means the book has never published a snapshot.

<Warning>
  `tickerState` comes from the last published order-book snapshot, so it is only as fresh as that
  market's snapshot cadence, and a market that stops publishing keeps reporting its last phase.
  Treat it as the last observed phase, not a fact about this instant, and never use it as the sole
  gate on submission, because the engine's own rejection is authoritative.
</Warning>
