# N1 Documentation > Documentation for N1, an L1 blockchain for global, high-performance, trustless finance. This file contains all documentation content in a single document following the llmstxt.org standard. ## Signed actions Every state change — orders, cancels, withdrawals, transfers, triggers, session management — is an `Action`: a protobuf message, signed, submitted to a single endpoint. If you use the [TypeScript SDK](/developers/typescript-sdk/) this is handled for you; this page is what you need to implement it elsewhere. ## The schema is served by the exchange ```bash curl -sO https://api-mainnet.n1.xyz/schema.proto ``` Generate bindings from that file rather than from a vendored copy, so your client matches the deployment's version. Check the version you are talking to with `GET /info` and the [Changelog](/developers/changelog). ## Submitting ```text POST /action Content-Type: application/octet-stream ``` The body is the length-delimited protobuf encoding of the `Action`, immediately followed by the signature over those encoded bytes: ```text body = size_delimited(Action) || signature ``` The response is a length-delimited `Receipt`. A receipt whose kind is `err` carries an `Error` enum value — an HTTP 200 with an error receipt is a rejected action, not a success, so decode the receipt before treating a submission as executed. The encoded action, including the signature, must be **1024 bytes or less**. Batch with care: an atomic action containing many subactions can exceed the limit. ## Required fields Every `Action` carries: | Field | Value | | --- | --- | | `currentTimestamp` | Server time from `GET /timestamp`, not local clock time | | `nonce` | Monotonic per signer; replayed or stale nonces are rejected | | `kind` | The specific action (place order, cancel, withdraw, …) | Recover state after a restart with `GET /event/last-acked-nonce` for your signer and `GET /action/last-executed-id`. Using the client clock instead of `/timestamp` is the most common cause of otherwise-valid actions being rejected. ## Signing Actions are signed either by the wallet key or by a session key authorized by that wallet. Sessions exist so a trading process never holds the wallet key — see [Accounts and sessions](/developers/getting-started/accounts-and-sessions). Session-signed actions carry the `sessionId` inside the action kind. Session creation supports two signature framings, `hex` and `solanaTransaction`, the latter for wallets that will only sign a Solana transaction rather than an arbitrary message. ## Units All prices, sizes, and amounts in actions are scaled integers, using the market's `priceDecimals` / `sizeDecimals` or the token's `decimals` from `GET /info`. Quote notional amounts scale by `priceDecimals + sizeDecimals`. `u128` values are carried as `hi` / `lo` pairs. Sending a human-readable decimal where an integer is expected is silently a very different order. ## Checklist 1. Fetch `/info` once; cache market and token IDs and decimals. 2. Fetch `/timestamp` before signing. 3. Track your nonce. 4. Encode length-delimited, sign, append the signature. 5. `POST /action` as `application/octet-stream`. 6. Decode the `Receipt`, and check for `err`. --- ## REST API The REST API is read-only apart from `POST /action`, which is how every state change is submitted — see [Signed actions](/developers/api/actions). No authentication is needed for reads. Base URLs are listed in [Networks and endpoints](/developers/getting-started/networks). The interactive, always-current reference is generated from the live schema: [Nord API and Proton API](/api?api=nord). ## Exchange and markets | Endpoint | Purpose | | --- | --- | | `GET /info` | Markets, tokens, decimals, and IDs — fetch this first | | `GET /markets/live` | Live pricing state for every market | | `GET /market/{market_id}/live` | Live pricing state for one market | | `GET /market/{market_id}/orderbook` | Orderbook snapshot with an `updateId` | | `GET /market/{market_id}/stats` | Rolling market statistics | | `GET /market/{market_id}/history/PT1H` | Hourly market history | | `GET /trades` | Trades, filterable and paginated | | `GET /order/{order_id}` and `/order/{order_id}/trades` | One order and its fills | | `GET /tokens/{token_id}/stats` | Token statistics | | `GET /fee/brackets/info` | Fee tier brackets | | `GET /timestamp` | Server time, required when signing actions | ## Accounts | Endpoint | Purpose | | --- | --- | | `GET /user/{pubkey}` | Accounts owned by a wallet | | `GET /account/{account_id}` | Balances, positions, and margin state | | `GET /account/{account_id}/orders` | Order history | | `GET /account/{account_id}/orders/open` | Open orders | | `GET /account/{account_id}/position/summary` | Position summary | | `GET /account/{account_id}/pnl/summary` | Realized and unrealized PnL | | `GET /account/{account_id}/history/{kind}` | Paginated history | | `GET /account/{account_id}/triggers` | Triggers on the account | | `GET /account/{account_id}/fee/tier` | Effective fee tier | | `GET /account/{account_id}/fees/withdrawal` | Quoted withdrawal fee | | `GET /event/last-acked-nonce` | Highest acknowledged nonce for a signer | | `GET /action/last-executed-id` | Last executed action ID | History `kind` is one of `deposit`, `withdrawal`, `transfer`, `funding`, `pnl`, `position`, `liquidation`, `fee-tier`, `vault/deposit`, or `vault/withdrawal`. Accounts are addressed by numeric account ID, not by wallet address — see [Accounts and sessions](/developers/getting-started/accounts-and-sessions) for how to resolve one. ## Vaults `GET /vaults`, `GET /vault/{vault_id}`, `GET /vault/{vault_id}/user/{account_id}`, and `GET /account/{account_id}/vault-user-states`. ## Conventions - **Scaled integers.** Prices, sizes, and amounts are integers scaled by the market's `priceDecimals` / `sizeDecimals` or the token's `decimals`. Read them from `/info` and descale before displaying. A `BTCUSD` price of `724336` with `priceDecimals: 1` is `72433.6`. - **Pagination.** History endpoints take `pageSize` and a cursor (`startInclusive`), plus `since` / `until` RFC3339 bounds where applicable. - **Timestamps.** RFC3339 in requests; `/timestamp` is the authority for signing. - **Schemas.** `GET /openapi.json`, `GET /proton/openapi.json`, and `GET /schema.proto` are served by every deployment, so you can generate clients and protobuf bindings for the exact version you are talking to. For anything latency-sensitive, stream instead of polling — [WebSocket streams](/developers/api/websocket-streams). --- ## WebSocket streams Streams are selected in the URL path, not by a subscribe message. Connect to: ```text wss://api-mainnet.n1.xyz/ws/[&...] wss://api-devnet.n1.xyz/ws/[&...] ``` Multiple streams are joined with `&` in the path segment, so one connection can carry everything a process needs. ## Streams | Stream | Form | Contents | | --- | --- | --- | | Trades | `trades@` | Executed trades for a market | | Orderbook deltas | `deltas@` | Incremental book updates for a market | | Candles | `candle@:` | OHLCV bars | | Account | `account@` | Balance, position, and order updates | | RFQ fills | `rfq-fills@` | RFQ fill requests and results | | Vault | `vault@` | Vault state updates | | Liquidations | `liquidations` | Liquidation events, exchange-wide | Resolutions are `1`, `5`, `15`, `30`, `60`, `4H`, `1D`, `1W`, `1M`. Example — trades and deltas for BTCUSD plus one account, on one socket: ```text wss://api-mainnet.n1.xyz/ws/trades@BTCUSD&deltas@BTCUSD&account@42 ``` ## Message envelope Messages are JSON and identify their stream, so a multiplexed connection can be dispatched on the stream name. Values arrive as scaled integers: divide by the market's `priceDecimals` / `sizeDecimals` or the token's `decimals` from [`/info`](/developers/api/rest). ## Keeping a book in sync Deltas are incremental and carry an `updateId`. Buffer deltas, take a snapshot from `GET /market/{market_id}/orderbook`, drop buffered updates at or below the snapshot's `updateId`, then apply the rest in order. Re-snapshot if `updateId` continuity breaks. ## Reconnecting Assume disconnects. On reconnect, re-open the same stream URL and re-establish state from REST before trusting incremental updates: a snapshot for books, and account state for balances, positions, and orders. The [TypeScript SDK](/developers/typescript-sdk/websockets) handles reconnection and provides typed handlers if you would rather not build this yourself. --- ## Changelog ## v20.0.0 **devnet**: **mainnet**: This release activates RFQ markets on mainnet and makes fee configuration aware of market type and execution mode. Existing CLOB order placement remains compatible, but RFQ, fee-administration, liquidation, and raw protobuf integrations must migrate to the v20 shapes below. - **BREAKING protobuf, RFQ requests:** `RfqPlaceOnly.base` and the nested `FillType` and `PartialFill` messages are removed. Send `size`, optional `minimal_fill`, and optional `is_reduce_only` directly on `RfqPlaceOnly`. `RfqTrade` now carries optional `minimum_size`, `maximum_size`, and `is_reduce_only` directly. - **BREAKING protobuf, RFQ results:** `RfqFillStatus` and `FillRFQResult.status` are removed. Determine whether a real execution occurred from the optional `execution` payload. Sampling responses omit it. - **BREAKING protobuf, fee administration:** replace `AddFeeTier`, `UpdateFeeTier`, and `UpdateAccountsTier` with `AddFeeTierType`, `UpdateFeeTierType`, and `UpdateAccountsFeeTierTypeMode`. The replacement `FeeTierTypeConfig` has separate CLOB and RFQ maker and taker rates for each market type. Regenerate protobuf bindings because the new action and receipt cases use new field numbers. - **BREAKING protobuf, liquidations:** `TakeAllPositionsResult` now contains a repeated `takers` list. Each taker entry contains its account ID, balances, positions, and order updates. Consumers must iterate the list instead of reading one top-level taker. - protobuf: `SetBackstopAccount` and its receipt now include `market_mode`, and the `Error` enum adds `CONFIG`. - API: market-live responses can include separate `rfq` and `clob` sections. Historical order rows now include `clientOrderId`, and `GET /account/{account_id}/orders/open` returns current open-order history records. These additions do not remove the existing account or market routes. - **BREAKING TypeScript SDK:** RFQ partial-fill sizes are decimal values, `NordUser.takePositions()` returns `takers`, and fee-tier admin methods use the new market-type-aware protobuf actions. Upgrade to `@n1xyz/nord-ts@0.7.4` before submitting v20 RFQ, liquidation, or fee-administration actions. To migrate, regenerate protobuf code from the v20 schema, update exhaustive action and receipt switches for the new cases, replace the removed RFQ wrapper messages, and add fixtures for multi-taker liquidation receipts. Applications that only read existing CLOB account, market, orderbook, and WebSocket fields do not need a wire-format migration. nord-ts version: `0.7.4` ## v19.0.0 **devnet**: **mainnet**: This release includes breaking changes to atomic actions as well as backend support for our upcoming RFQ feature. - **BREAKING** protobuf: Atomic V2 is removed. `Action.atomic_v2`, `AtomicV2`, `AtomicSubactionV2`, and `AtomicPlacementRequest` are no longer available. Regenerate your protobuf bindings and send `Action.atomic`. - Put each `AtomicSubaction` directly in `Atomic.actions`. - Replace `delegator_account_id` with `target_account_id`. (field number remains `32`) - Move `ReduceOrderLiquidation.target_account_id` to its parent `AtomicSubaction`. - For self-targeted actions, omit `target_account_id` and `use_key`. - For vault actions, use `vault_account_id` as the target and set `use_key = true`. - For liquidation actions, use the liquidation account ID as the target and leave `use_key` unset or `false`. - **BREAKING** protobuf: `VAULT_PREVIOUS_EPOCH_WITHDRAWALS_NOT_PAYABLE` is removed from the `Error` enum. - protobuf: `SpecialAccount` now includes `CLAIM_VAULT`. `VaultSyncEpochsReceipt` now includes `vault_account_id`. - **BREAKING** ts: Atomic subactions now use `targetAccountId` and `useKey`. Remove `delegatorAccountId` and `placement`. - **BREAKING** ts: Use `await nord.getProtonClient()`. Direct access through `nord.protonClient` is no longer available. Proton initialization now starts on first use. - **BREAKING** api: `GET /account/system` now returns `{ actionId, feeVault, claimVault }`. It previously returned the fee-vault balance array. - api: Trade and order history responses now include `marketMode`. - ws: Account `fills` keys now use `orderId:makerId:rawPrice`. Code that reads `Object.values(fills)` continues to work. Code that looks up fills by key must change. nord-ts version: `0.7.3` ## v18.0.0 **devnet**: **mainnet**: - **BREAKING**: api: `GET /account/{account_id}` now returns open-order `clientOrderId` as `string | null` instead of `number | null`. - **BREAKING**: api: WebSocket `client_order_id`, `taker_client_order_id[]`, and deprecated `sender_tracking_id` values now serialize as numeric strings instead of numbers. - Added `RefreshSession` to allow a session to refresh itself up to some maximum deadline while maintaining the same session ID. - Added `SelfRevoke` to allow a session to revoke itself. - `CreateSession` now accepts an optional `refresh_deadline` parameter and returns the effective expiry and refresh deadline. - ts: `NordUser.refreshSession()` now returns `{ sessionId, expiry, refreshDeadline? }` instead of `void`. - api: `GET /user/{pubkey}` session objects now include optional `refreshDeadline`. nord-ts version: `0.7.0` to `0.7.2` ## v17.0.0 **devnet**: **mainnet**: * atomic actions are expanding: trigger add/edit/remove is moving into atomic actions * trigger history pagination now supports sub_action_id * increased action timestamp staleness threshold from 60s to 120s * relaxed oracle unfreeze price-change threshold from 0.5% to ~1.33% per timestamp unit * added referrer tracking, i.e. builder codes * added taker-side client order ID to WebSocket messages * rate now has two buckets: `/action` and all other endpoints. we will be announcing new rate limits as we adjust them * introduced vaults. see docs for api. briefly: * HTTP: * `GET /vaults/*` * `GET /account/{account_id}/vault-user-states` * `GET /account/{account_id}/history/vault/*` * WS: * subscribe with `vault@{vault_id}` * receives Vault update payload with latest full vault state nord-ts v0.5.1: * adds centralized math utilities and updated trading function types * adds trigger add/edit/remove support in atomics nord-ts v0.6.0: * introduces ActionsV2 with the new placement interface instead of delegator_id * placement must now be explicit, see the module docs * user actions for deposit and withdrawing to/from vaults * on refresh session you can specify expiry timestamp both `v0.5.1` and `v0.6.0` are compatbile with v17 of nord. --- ## Accounts and sessions ## Wallets and accounts A Solana wallet owns one or more Nord accounts. Accounts hold balances, positions, and orders, and are addressed by a numeric `accountId`. A wallet's accounts are resolved from its public key: - REST: `GET /user/{pubkey}` returns the account IDs for a wallet. - SDK: `await user.updateAccountId()` populates `user.accountIds`. Account-scoped state is then read with `GET /account/{account_id}` and the related endpoints, or with `await user.fetchInfo()`. Accounts owned by the same wallet are [subaccounts](/trading/accounts/subaccounts) — separate margin and positions under one wallet. Every action that can target an account takes an optional `accountId`, and the SDK defaults to the first account of the wallet. ## Sessions Signing every order with a wallet key would be slow and would keep the wallet key hot. Instead, a wallet authorizes a **session key**: an ephemeral keypair allowed to submit actions on its behalf until the session expires. The flow: 1. The wallet signs a session-creation request naming the session public key and an expiry. 2. The session key signs subsequent actions — orders, cancels, withdrawals, transfers. 3. Before expiry, the session is refreshed; otherwise it lapses and actions are rejected. With the SDK: ```typescript const user = NordUser.fromPrivateKey(nord, process.env.PRIVATE_KEY!); await user.updateAccountId(); await user.refreshSession(); // create or replace the session await user.selfRefreshSession(); // extend the current session await user.selfRevoke(); // end the current session await user.revokeSession(sessionId); // revoke a session by id, from the wallet ``` `refreshSession(expiryTimestamp?, refreshDeadline?)` accepts an explicit expiry; without arguments it uses the SDK's default session lifetime. Order-entry calls throw if there is no valid session, so treat "refresh before expiry" as part of your run loop rather than something to retry after a failure. `NordUser.fromPrivateKey` generates the session keypair for you. Browser integrations construct `NordUser` with signing callbacks from a wallet adapter, so the wallet only ever signs session creation, not each order. Two signature framings are supported for session creation: hex message signing and Solana transaction framing, for wallets that will only sign transactions. ## Nonces and action IDs Actions are ordered per account and carry a nonce, and each accepted action returns an `actionId`. The SDK tracks nonces internally; direct integrations read them from the API: | Purpose | Endpoint | | --- | --- | | Current exchange timestamp | `GET /timestamp` | | Last acknowledged nonce | `GET /event/last-acked-nonce` | | Last executed action ID | `GET /action/last-executed-id` | See [Signed actions](/developers/api/actions) for how these are assembled into a submittable action. ## Operational guidance - Keep the wallet key offline where possible; ship only the session key to the process that trades. - Give sessions the shortest lifetime that fits your refresh loop. - Revoke sessions when a process is retired — a revoked session cannot submit actions even if its key leaks. - Never commit keys or session material. Load them from your secret store. --- ## Networks and endpoints ## Base URLs | Network | API base URL | WebSocket base URL | | --- | --- | --- | | Mainnet | `https://api-mainnet.n1.xyz` | `wss://api-mainnet.n1.xyz` | | Devnet | `https://api-devnet.n1.xyz` | `wss://api-devnet.n1.xyz` | Develop against devnet first: it runs the same protocol version as mainnet ahead of each release, so it is where breaking changes appear first. Schemas are served by each deployment, so they always match the version you are talking to: | Resource | Path | | --- | --- | | Nord OpenAPI schema | `/openapi.json` | | Proton OpenAPI schema | `/proton/openapi.json` | | Action protobuf schema | `/schema.proto` | | Exchange configuration | `/info` | | WebSocket streams | `/ws/{streams}` | The [API references](/api?api=nord) render the same OpenAPI schemas interactively. Each network also needs a Solana RPC endpoint for deposits, which settle on Solana rather than on Nord: mainnet-beta for mainnet, devnet for devnet. ## Identifiers, decimals, and scaling `GET /info` returns the markets and tokens the deployment is running, and it is the source of truth for every identifier you pass to the API: ```json { "markets": [ { "marketId": 0, "symbol": "BTCUSD", "priceDecimals": 1, "sizeDecimals": 5, "baseTokenId": 0, "quoteTokenId": 0, "mode": "clob", "regime": "normal" } ], "tokens": [ { "tokenId": 0, "symbol": "USDC", "decimals": 6, "mintAddr": "..." } ] } ``` - `marketId` and `tokenId` are the numeric identifiers used by actions and by most endpoints. Symbols like `BTCUSD` are used by WebSocket streams. - `priceDecimals` and `sizeDecimals` set the tick and lot precision of a market, and are the scale factors for prices and sizes on the wire: an action carries `price × 10^priceDecimals` and `size × 10^sizeDecimals` as integers. Quote amounts are scaled by `priceDecimals + sizeDecimals`. - `decimals` on a token scales deposit, withdrawal, and transfer amounts. - `mode` is the market's [execution mode](/trading/markets/execution-modes) (`clob` or RFQ), which determines how orders are matched. Do not hardcode IDs or decimals. Read `/info` at startup and refresh it after a protocol release; markets and tokens are added over time and decimals can change between deployments. The TypeScript SDK reads `/info` during `Nord.new` and applies these scale factors for you, so SDK calls take human-readable decimal prices and sizes. --- ## Quickstart This walkthrough uses the TypeScript SDK against devnet. Prerequisites: Node 20 or newer and a funded Solana devnet keypair. ## 1. Install ```bash npm install @n1xyz/nord-ts @solana/web3.js ``` ## 2. Read market data No account, session, or key is needed to read state. ```typescript import { Connection } from "@solana/web3.js"; import { Nord } from "@n1xyz/nord-ts"; const nord = await Nord.new({ webServerUrl: "https://api-devnet.n1.xyz", app: process.env.APP_ADDRESS!, solanaConnection: new Connection("https://api.devnet.solana.com"), }); console.log(nord.markets.map((m) => [m.marketId, m.symbol])); const orderbook = await nord.getOrderbook({ marketId: 0 }); console.log(orderbook.bids[0], orderbook.asks[0]); ``` `Nord.new` fetches `/info`, so `nord.markets` and `nord.tokens` carry the market and token configuration of the deployment, including the decimals the SDK uses to scale prices and sizes. ## 3. Create a user and open a session Order entry is authorized by a session key, not by your wallet key on every action. See [Accounts and sessions](/developers/getting-started/accounts-and-sessions). ```typescript import { NordUser } from "@n1xyz/nord-ts"; const user = NordUser.fromPrivateKey(nord, process.env.PRIVATE_KEY!); await user.updateAccountId(); // resolve the accounts owned by this wallet await user.fetchInfo(); // load balances, positions, and open orders await user.refreshSession(); // register the session key ``` `updateAccountId` throws if the wallet is not known to the exchange yet. Fund it with a deposit, then re-run it once the deposit has been processed: ```typescript await user.depositSpl(100, 0); // 100 USDC (tokenId 0) await user.updateAccountId(); ``` ## 4. Place and cancel an order ```typescript import { FillMode, Side } from "@n1xyz/nord-ts"; const { orderId, fills } = await user.placeOrder({ marketId: 0, side: Side.Bid, fillMode: FillMode.Limit, isReduceOnly: false, size: 0.001, price: 50000, }); console.log({ orderId, fills }); if (orderId !== undefined) { await user.cancelOrder(orderId); } ``` `orderId` is only present when part of the order rests on the book — a fully filled immediate order returns `fills` and no `orderId`. ## 5. Follow your account ```typescript const accountSub = nord.subscribeAccount(user.accountIds![0]); accountSub.on("message", (update) => console.log(update)); accountSub.on("error", (error) => console.error(error)); ``` ## Next steps - [Trading](/developers/typescript-sdk/trading) — order types, reduce-only, client order IDs, self-trade prevention. - [Market data](/developers/typescript-sdk/market-data) — orderbooks, trades, stats, historical queries. - [WebSockets](/developers/typescript-sdk/websockets) — combined subscriptions and reconnect behavior. - [Direct API integration](/developers/api/rest) — if you are not using TypeScript. --- ## Guide Nord is the exchange engine behind N1. Integrations read state over HTTP and WebSocket and change state by submitting signed protobuf actions to a single endpoint. Everything the web app does is available to you over the same interfaces. Two integration paths: - **TypeScript** — [`@n1xyz/nord-ts`](https://www.npmjs.com/package/@n1xyz/nord-ts) wraps the protocol, including signing, decimal scaling, and WebSocket subscriptions. Fastest way to get running. - **Any language** — call the [REST API](/developers/api/rest), stream [WebSocket updates](/developers/api/websocket-streams), and build [signed actions](/developers/api/actions) from the published protobuf schema. ## Start here - [Quickstart](/developers/getting-started/quickstart) — read a market and place your first order. - [Networks and endpoints](/developers/getting-started/networks) — devnet and mainnet base URLs, market and token IDs, decimals. - [Accounts and sessions](/developers/getting-started/accounts-and-sessions) — how authorization works before you can trade. ## Reference - [TypeScript SDK](/developers/typescript-sdk/) — client setup, market data, trading, RFQ, triggers, funding, WebSockets. - [API](/developers/api/rest) — REST endpoints, streams, and the action protocol. - [API references](/api?api=nord) — interactive Nord and Proton OpenAPI references, generated from the live schemas. - [Changelog](/developers/changelog) — protocol releases and breaking changes. - [Resources](/developers/resources) — packages, schemas, and support. For product behavior — margining, funding, liquidations, fees, order semantics — see the [Trading](/trading/markets/perpetual-markets) section. This section covers how to talk to the exchange, not how the exchange prices risk. --- ## Resources ## Packages | Package | Purpose | | --- | --- | | [`@n1xyz/nord-ts`](https://www.npmjs.com/package/@n1xyz/nord-ts) | TypeScript SDK: signing, decimal scaling, REST and WebSocket clients | ## References - [Nord API reference](/api?api=nord) — interactive, generated from the live OpenAPI schema. - [Proton API reference](/api?api=proton) — interactive Proton reference. - Raw schemas, per deployment: - `https://api-mainnet.n1.xyz/openapi.json` - `https://api-mainnet.n1.xyz/proton/openapi.json` - `https://api-mainnet.n1.xyz/schema.proto` - The same paths exist on `https://api-devnet.n1.xyz`. - [Changelog](/developers/changelog) — protocol releases and breaking changes. ## Build against devnet first Devnet runs the same API surface as mainnet with test funds, so integration work never needs real capital. Endpoints and Solana cluster settings are in [Networks and endpoints](/developers/getting-started/networks). ## Product documentation The [Trading](/trading/markets/perpetual-markets) section covers what the exchange does — margining, funding, liquidations, fees, and order semantics. Read it alongside these pages: knowing the API is not the same as knowing how a position is priced or when it gets liquidated. ## Getting help Ask in [Discord](https://discord.gg/n1chain), and include the network, market, and — where relevant — the action ID or order ID from the receipt. Those IDs make an issue reproducible. --- ## Funding and transfers Deposits move tokens from Solana into the exchange and therefore settle as Solana transactions. Everything else — withdrawals, transfers, vault operations — is a signed Nord action. Product-level behavior is described in [Deposits and Withdrawals](/trading/accounts/deposits-and-withdrawals). ## Deposit ```typescript const signature = await user.depositSpl(100, 0); // 100 USDC (tokenId 0) // Or with the full result, including the buffer account used to correlate it: const { signature: sig, buffer } = await user.deposit({ amount: 100, tokenId: 0, recipient, // optional; defaults to the user's own address }); ``` Deposits require a working Solana RPC connection (the `solanaConnection` passed to `Nord.new`) and an associated token account for the mint. A deposit is credited once it is processed by the exchange, so poll account state or watch the account stream rather than assuming the balance is available when the Solana signature confirms. Deposit history is available at `GET /account/{account_id}/history/deposit`. ## Withdraw ```typescript const { actionId } = await user.withdraw({ tokenId: 0, amount: 100, destPubkey, // optional; defaults to the user's wallet }); ``` Withdrawals are subject to a withdrawal fee and to the account's [margin requirements](/trading/margining/margin). Quote the fee first: ```typescript const fee = await nord.getAccountWithdrawalFee(accountId); ``` History: `GET /account/{account_id}/history/withdrawal`. ## Transfer between accounts ```typescript // Between accounts owned by the same wallet (subaccounts) await user.transferOwned({ tokenId: 0, amount: 25, fromAccountId, toAccountId }); // To an account you do not own await user.transferUnowned({ tokenId: 0, amount: 25, fromAccountId, toAccountId }); ``` Omitting `toAccountId` on `transferOwned` moves funds to a new owned account, and the result's `newAccountId` reports it. History: `GET /account/{account_id}/history/transfer`. ## Vaults ```typescript await user.vaultDeposit({ vaultId, amount: 100 }); await user.vaultWithdrawRequest({ vaultId, kind: "quoteNotional", amount: 50 }); await user.vaultWithdrawClaim({ vaultId }); await user.vaultSyncEpochs({ vaultId }); ``` Vault withdrawals are two-step: a request, then a claim once the vault's epoch allows it. `kind` selects how the requested amount is interpreted (for example `"quoteNotional"`). Vault state is available at `GET /vaults`, `GET /vault/{vault_id}`, `GET /vault/{vault_id}/user/{account_id}`, and `GET /account/{account_id}/vault-user-states`. All amounts on this page are decimals; the SDK scales them by the token's `decimals` from `/info`. --- ## Overview [`@n1xyz/nord-ts`](https://www.npmjs.com/package/@n1xyz/nord-ts) is the TypeScript client for Nord. It handles action signing, decimal scaling, nonce management, and WebSocket subscriptions, and exposes typed wrappers over the REST API. ## Install ```bash npm install @n1xyz/nord-ts @solana/web3.js # or: yarn add @n1xyz/nord-ts @solana/web3.js # or: bun add @n1xyz/nord-ts @solana/web3.js ``` ## Create a client ```typescript import { Connection } from "@solana/web3.js"; import { Nord } from "@n1xyz/nord-ts"; const nord = await Nord.new({ webServerUrl: "https://api-mainnet.n1.xyz", app: process.env.APP_ADDRESS!, solanaConnection: new Connection("https://api.mainnet-beta.solana.com"), }); ``` | Option | Required | Purpose | | --- | --- | --- | | `webServerUrl` | yes | Nord API base URL for the target [network](/developers/getting-started/networks) | | `app` | yes | App address you trade against | | `solanaConnection` | yes | Solana RPC connection, used for deposits | | `protonUrl` | no | Proton URL; defaults to `webServerUrl` | `Nord.new` fetches `/info`, so after it resolves the client already knows the deployment's markets and tokens: ```typescript nord.markets; // marketId, symbol, priceDecimals, sizeDecimals, mode, ... nord.tokens; // tokenId, symbol, decimals, mintAddr, ... ``` Because the client holds these decimals, SDK methods take ordinary decimal prices, sizes, and amounts and scale them to wire integers for you. :::note `Nord.initNord` is a deprecated alias of `Nord.new`, and the `initWebSockets` config flag is deprecated. New code should use `Nord.new` and create subscriptions explicitly. ::: ## Two objects - `Nord` — connection and read paths: exchange configuration, market data, account queries, WebSocket subscriptions. No key material. - `NordUser` — a wallet, its accounts, and its session; everything that submits a signed action. ```typescript import { NordUser } from "@n1xyz/nord-ts"; const user = NordUser.fromPrivateKey(nord, process.env.PRIVATE_KEY!); await user.updateAccountId(); await user.fetchInfo(); await user.refreshSession(); ``` After `fetchInfo`, `user.balances`, `user.positions`, `user.orders`, and `user.margins` are populated, each keyed by account ID: ```typescript const accountId = user.accountIds![0]; user.balances[accountId]; // [{ accountId, symbol, balance }, ...] user.positions[accountId]; user.orders[accountId]; // [{ orderId, marketId, side, size, price, ... }, ...] ``` This is a snapshot taken at fetch time, not a live view — refetch, or subscribe to account updates, after submitting actions. ## Errors SDK failures throw `NordError`, which wraps the underlying error in `cause`. Order-entry calls also throw when the session is missing or expired, so distinguish "session invalid" from "exchange rejected the order" when handling failures. ## Where to next - [Market data](/developers/typescript-sdk/market-data) - [Trading](/developers/typescript-sdk/trading) - [RFQ](/developers/typescript-sdk/rfq) - [Triggers](/developers/typescript-sdk/triggers) - [Funding and transfers](/developers/typescript-sdk/funding) - [WebSockets](/developers/typescript-sdk/websockets) --- ## Market data Market data needs no account, session, or key — only a `Nord` client. ## Exchange configuration ```typescript const info = await nord.getInfo(); // same payload Nord.new caches on the client nord.markets; // marketId, symbol, priceDecimals, sizeDecimals, mode, imf, mmf, ... nord.tokens; // tokenId, symbol, decimals, mintAddr, ... ``` Resolve `marketId` from a symbol through `nord.markets` rather than hardcoding it; IDs are per deployment. See [Networks and endpoints](/developers/getting-started/networks). ## Orderbook ```typescript const book = await nord.getOrderbook({ marketId: 0 }); // or by symbol: await nord.getOrderbook({ symbol: "BTCUSD" }) const [bestBidPrice, bestBidSize] = book.bids[0]!; const [bestAskPrice, bestAskSize] = book.asks[0]!; ``` Levels are `[price, size]` pairs, best first, already descaled to decimals. Each snapshot carries an `updateId`, which is how you order it against the incremental deltas from [WebSocket streams](/developers/typescript-sdk/websockets). ## Live prices and stats ```typescript const allLive = await nord.getMarketsLive(); const live = await nord.getMarketLive({ marketId: 0 }); const stats = await nord.getMarketStats({ marketId: 0 }); const tokenStats = await nord.getTokenStats(0); ``` Live and stats payloads carry the market's current pricing state — including the index and mark prices described in [Oracle, Index, and Mark Prices](/trading/markets/oracle-index-and-mark-prices) — and rolling market statistics. ## Trades ```typescript const recent = await nord.getTrades({ marketId: 0, pageSize: 50 }); const window = await nord.getTrades({ marketId: 0, since: "2026-08-01T00:00:00Z", // RFC3339, inclusive until: "2026-08-02T00:00:00Z", // RFC3339, exclusive pageSize: 500, }); ``` Filters: `marketId`, `takerId`, `makerId`, `takerSide` (`"bid"` / `"ask"`), `since`, `until`, `pageSize`. Paginate with `startInclusive`, which is a trade ID by default or an action ID when `paginationMode: "actionId"`. Non-RFC3339 timestamps throw before the request is sent. Per-order fills: ```typescript const order = await nord.getOrder(orderId); const fills = await nord.getOrderTrades(orderId); ``` ## Fees ```typescript const brackets = await nord.getFeeBrackets(); const tier = await nord.getAccountFeeTier(accountId); const quotedFee = await nord.getMarketFee({ marketId: 0, feeKind, accountId }); ``` `getMarketFee` quotes the fee for a fill role in quote-token units; a negative value is a fee charged. Fee tiers and rates are described in [Fees](/trading/orders/fees). ## Streaming Polling is fine for low-frequency reads. For anything latency-sensitive, subscribe instead — see [WebSockets](/developers/typescript-sdk/websockets). --- ## RFQ RFQ markets match a taker's request against a maker's quote instead of a continuous book. Product behavior — who can quote, how prices are validated, how funding is sampled — is described in [RFQ Markets](/trading/markets/rfq-markets). This page covers the SDK calls. ## Request a quote (taker) ```typescript import { Side } from "@n1xyz/nord-ts"; const { actionId, orderId } = await user.placeRfqOrder({ marketId, side: Side.Bid, size: 0.5, price: 50000, timeout: { seconds: 5 }, // optional request lifetime minimalFill: 0.1, // optional minimum acceptable fill isReduceOnly: false, // optional clientOrderId, // optional }); ``` `price` is the limit for the request: the resulting execution may be better, never worse. `orderId` identifies the request to makers. ## Fill a request (maker) Makers watch for requests and respond with `fillRfqOrder`: ```typescript const result = await user.fillRfqOrder({ marketId, orderId, price, minimumSize, // optional maximumSize, // optional isReduceOnly, // optional timeout, // optional }); ``` The result distinguishes a real trade from a price observation: ```typescript if (result.execution === null) { // Funding sampling only: no trade, no balance or position change. } else { const { filledSize, executionPrice, tradeId } = result.execution; } ``` `result` also carries `actionId`, `orderId`, `makerAccountId`, and `takerAccountId`. :::warning A `null` `execution` is not a failure and not a fill. Treating it as a trade double-counts volume and corrupts position tracking. This replaced the older `RfqFillStatus` field — see the [Changelog](/developers/changelog) entry for v20.0.0. ::: ## Watch for requests ```typescript const rfqSub = nord.subscribeRfqFills("BTCUSD"); // symbol or marketId rfqSub.on("message", (update) => console.log(update)); rfqSub.on("error", (error) => console.error(error)); ``` A maker loop typically subscribes to RFQ fill requests, prices each one from its own model, and calls `fillRfqOrder` while the request is still live. Requests expire, so respond within the request's timeout rather than retrying later. --- ## Trading Order entry requires a `NordUser` with a valid session — see [Accounts and sessions](/developers/getting-started/accounts-and-sessions). ## Place an order ```typescript import { FillMode, Side } from "@n1xyz/nord-ts"; const { actionId, orderId, fills, reducedOrders, selfTradeCancels } = await user.placeOrder({ marketId: 0, side: Side.Bid, fillMode: FillMode.Limit, isReduceOnly: false, size: 0.1, price: 50000, }); ``` | Parameter | Required | Notes | | --- | --- | --- | | `marketId` | yes | From `nord.markets` | | `side` | yes | `Side.Bid` or `Side.Ask` | | `fillMode` | yes | See below | | `isReduceOnly` | yes | Rejects or trims anything that would increase the position | | `size` | one of | Base size, in decimals | | `price` | one of | Limit price, in decimals | | `quoteSize` | one of | Bounds the order by quote amount instead of base size | | `accountId` | no | Defaults to the wallet's first account | | `clientOrderId` | no | Your own identifier, usable for cancels | | `selfTradePrevention` | no | `"expireMaker"` cancels the resting side instead of self-matching | | `referrer` | no | Referral identifier, see [Referrals](/trading/referrals) | At least one of `size`, `price`, or `quoteSize` must be provided. Prices and sizes are decimals; the SDK scales them to wire integers using the market's `priceDecimals` and `sizeDecimals`. ### Fill modes | `FillMode` | Behavior | | --- | --- | | `Limit` | Rests on the book after taking any crossing liquidity | | `PostOnly` | Never takes; rejected if it would cross | | `ImmediateOrCancel` | Takes what it can, cancels the remainder | | `FillOrKill` | Fills entirely or is cancelled | Semantics are documented in [Order Types](/trading/orders/order-types). ### Reading the result - `orderId` — present only if part of the order rests on the book. A fully filled `ImmediateOrCancel` or `FillOrKill` order has none. - `fills` — trades that executed immediately, with size, price, and trade ID. - `reducedOrders` — resting orders trimmed by this action (for example under reduce-only), with remaining and cancelled size. - `selfTradeCancels` — your own orders cancelled by self-trade prevention. Treat `orderId === undefined` as "nothing to cancel later", not as a failure. ## Cancel ```typescript await user.cancelOrder(orderId); await user.cancelOrderByClientId(clientOrderId); ``` Both accept an optional `accountId` and return `{ actionId, orderId, accountId }`. Using `clientOrderId` lets you cancel without persisting exchange order IDs — the common pattern for market makers restarting from their own state. ## Read your orders and positions ```typescript await user.fetchInfo(); const accountId = user.accountIds![0]; user.orders[accountId]; // open orders user.positions[accountId]; // positions user.balances[accountId]; // balances user.margins[accountId]; // margin state ``` For history and analytics, query through the client: ```typescript await nord.getAccountOrders(accountId, { pageSize: 100 }); await nord.getAccountPositionSummary(accountId); await nord.getAccountPnlSummary(accountId); await nord.getAccountPnl(accountId, { since, until, pageSize: 100 }); await nord.getAccountPositionHistory(accountId, { pageSize: 100 }); ``` Live updates come from the account stream — see [WebSockets](/developers/typescript-sdk/websockets). ## Batching related actions `user.atomic([...])` submits several subactions as one action, so they succeed or fail together — for example replacing a quote by cancelling and re-placing in a single round trip. `user.placeRfqOrder` is built on it. ## Liquidations `user.takePositions({ targetAccountId })` takes over the balances and positions of an eligible account and returns the taken balances and positions per taker. This is the integration point for liquidators; eligibility and pricing are described in [Liquidations](/trading/margining/liquidations). ## Related - [Triggers](/developers/typescript-sdk/triggers) — stop-loss and take-profit. - [RFQ](/developers/typescript-sdk/rfq) — request-for-quote markets. - [Fees](/trading/orders/fees) — what a fill costs. --- ## Triggers Triggers are conditional orders held by the exchange and submitted when the market reaches a trigger price. Product behavior is described in [Take Profit and Stop Loss](/trading/orders/take-profit-and-stop-loss). ## Add a trigger ```typescript import { Side, TriggerKind } from "@n1xyz/nord-ts"; const { actionId, triggerId } = await user.addTrigger({ marketId: 0, side: Side.Ask, kind: TriggerKind.StopLoss, // or TriggerKind.TakeProfit triggerPrice: 45000, limitPrice: 44900, // optional; omit for a market-style exit limitBaseSize: 0.1, // optional limitQuoteSize: undefined, // optional alternative to limitBaseSize accountId, // optional }); ``` `triggerPrice` must be positive. Size the resulting order with either `limitBaseSize` or `limitQuoteSize`. ## Edit and remove `editTrigger` replaces the trigger's parameters, so pass the full definition — `triggerId`, `marketId`, `side`, `kind`, `triggerPrice`, and any limit fields — not just the fields you are changing. ```typescript await user.editTrigger({ triggerId, marketId: 0, side: Side.Ask, kind: TriggerKind.StopLoss, triggerPrice: 44000, limitBaseSize: 0.1, }); await user.removeTrigger({ marketId: 0, triggerId }); ``` ## Query triggers | Purpose | Endpoint | | --- | --- | | Triggers on an account | `GET /account/{account_id}/triggers` | | Placement history | `GET /account/{account_id}/triggers/history/placements` | | Finalisation history | `GET /account/{account_id}/triggers/history/finalisations` | | Active triggers, exchange-wide | `GET /triggers/active` | A trigger is `Active` until it fires (`Success`), is cancelled, or is removed, so reconcile against the placement and finalisation history rather than assuming a trigger is still live. --- ## WebSockets Every subscription helper returns an event emitter with `"message"` and `"error"` events and a `close()` method. ```typescript const sub = nord.subscribeTrades("BTCUSD"); sub.on("message", (update) => console.log(update)); sub.on("error", (error) => console.error(error)); // later sub.close(); ``` ## Helpers | Call | Stream | | --- | --- | | `nord.subscribeOrderbook(symbol)` | Orderbook deltas for one market | | `nord.subscribeTrades(symbol)` | Trades for one market | | `nord.subscribeBars(symbol, resolution)` | Candles for one market | | `nord.subscribeRfqFills(symbolOrMarketId)` | RFQ fill requests for one market | | `nord.subscribeAccount(accountId)` | Balance, position, and order updates | Symbols are the market symbols from `/info` (for example `BTCUSD`), not market IDs. Candle resolutions are `"1"`, `"5"`, `"15"`, `"30"`, `"60"`, `"4H"`, `"1D"`, `"1W"`, and `"1M"` (minutes where numeric). Helpers filter by symbol or account for you, so a handler only sees updates for the market or account it subscribed to. ## One connection, many streams Each helper opens its own connection. To multiplex, create the client directly: ```typescript const ws = nord.createWebSocketClient({ trades: ["BTCUSD", "ETHUSD"], deltas: ["BTCUSD"], candles: [{ symbol: "BTCUSD", resolution: "60" }], accounts: [accountId], rfqFills: ["BTCUSD"], liquidations: true, }); ``` This is the right shape for a market-making or monitoring process: one socket carrying every stream it needs, instead of one socket per stream. Account IDs must be positive; invalid IDs throw before the socket is opened. ## Applying orderbook deltas Delta updates are incremental. To keep a local book: 1. Subscribe to deltas and buffer updates. 2. Fetch a snapshot with `nord.getOrderbook({ marketId })`. 3. Discard buffered updates at or below the snapshot's `updateId` and apply the rest in order. 4. If `updateId` continuity breaks, re-snapshot rather than patching. ## Underlying protocol The helpers wrap `/ws/{streams}` — see [WebSocket streams](/developers/api/websocket-streams) for the raw stream names and message shapes, which is what you need if you are not using the SDK. --- ## About the team [Null Studios](https://nullstudios.xyz) is a core contributor to the N1 network. Its founders have known each other since high school. They went on to Harvard and McGill, entered crypto in 2019, and have been building low-latency onchain trading and orderbook systems since 2020. Building these systems exposed the same problems again and again: infrastructure that failed when reliability mattered most, fragmented liquidity and margin that made capital inefficient, and general-purpose systems never designed for demanding financial workloads. That experience led the team to found Null Studios and build N1. Today, N1 is backed by leading institutions in technology and trading—including Peter Thiel's Founders Fund, IMC, Amber, GSR, and others—all united by the same vision: for N1 to become the global blockchain for trustless finance. --- ## Introduction to N1 N1 is an L1 blockchain for global, high-performance, trustless finance. The network embeds financial modules such as an orderbook, RFQ, and a margin system directly in the network layer, secured by validators. Everything on N1 is designed around one north star: becoming the best and most capital-efficient venue to trade. ## One North Star: Maximum Capital-efficiency Capital efficiency is not a single feature. It depends on execution quality, reliable access to markets, and a margin system that uses collateral effectively. N1 is designed around all three: - **Best-in-class trading infrastructure.** N1 combines an ultra-low-latency matching engine with horizontal scalability. Sharded data availability allows throughput to grow with validator capacity. Rather than optimizing for general-purpose workloads, the blockchain is purpose-built for trading and high-frequency finance, with infrastructure and validator nodes designed to make trading a first-class network action. - **Zero-congestion architecture.** Financial modules operate independently from unrelated programs, so other network activity does not compete with trading for execution capacity. - **Advanced margining.** N1's margin system is designed to use collateral efficiently across positions. Portfolio margining is currently in private beta testing. These capabilities are supported by N1's architecture. Unlike most blockchains, N1 does not leave core financial primitives to be reassembled out of contracts. It makes them native: they live in the network layer itself and are secured by validators. Embedding these modules at the network layer allows N1 to pursue high performance and capital efficiency without giving up trustless operation. The base layer provides data availability and remains programmable, allowing the network to add more native financial modules over time. ## Native financial modules N1's financial primitives are part of the network, not applications bolted on top. Each module is validator-secured, so it inherits the same trust and security guarantees as the chain itself. - **Orderbook.** A native central limit orderbook with an ultra-low-latency matching engine. - **RFQ.** A native request-for-quote system for sourcing competitive quotes and executing larger or less liquid trades. - **Margin system.** Native margining and collateral management designed to use capital efficiently across positions. More modules will follow, extending the network's financial surface area over time. Because they are native and validator-secured, modules compose with shared security and shared liquidity rather than fragmenting it. ## Dive deeper For the complete architecture and long-term vision, read the [litepaper](https://docsend.com/view/bvu594ex3di55gsh). The Learn pages break the system into settlement, execution, and transaction lifecycle pieces so you can follow the layer that matters most to your work. --- ## Execution N1 is agnostic to execution environments. Apps can run in specialized machines, general-purpose VMs, or language-native runtimes while communicating through ordered channels. ## Overview N1's default execution environment is an asynchronous network of virtual machines called processes. Each process can run a different VM and send messages to other processes through ordered channels. For example, one process might be a TypeScript perpetuals exchange and another might be a Solidity contract. Both can interoperate without sharing one global state machine. ![N1 execution processes](/img/learn/protocol/execution-processes.png) The gate app handles bridging assets from settlement and routing them to the correct app in the execution network. This simplifies topology and supports fast liquidity between apps. ## Unique properties N1's default execution network is unusual because it is designed to use the base layer for high-throughput data availability while letting apps specialize their own compute. ### Dedicated compute Existing blockchains run all applications on a shared VM. Sudden load creates state contention, gas spikes, and latency variance. That limits complex apps and makes developers optimize around chain constraints instead of product requirements. In N1, each program runs in an isolated dedicated compute environment. That environment can use the full resources assigned to it and scale independently from unrelated apps. Deploying can still feel familiar. Solidity developers can use Foundry, Rust developers can use WASM tooling, and TypeScript developers can use the tools they already know. ![N1 dedicated compute environments](/img/learn/protocol/execution-dedicated-compute.png) ### Instantaneous communication Programs communicate point-to-point over ordered channels with binary messages. Each message triggers the receiver to execute the payload. ![N1 instantaneous communication](/img/learn/protocol/execution-instant-communication.png) Unlike many bridge-style messaging systems, N1 messages can be executed as soon as they land in the destination process. Apps do not need to wait for a separate external bridge validation step for ordinary cross-app communication. ![N1 bridge-style messaging comparison](/img/learn/protocol/execution-bridge-messaging.png) ### SNARK fraud proofs A SNARK is a compact proof of computation. N1 uses this direction to support efficient execution today while preserving a path toward stronger verification over time. In the optimistic model, SNARKs can be used as fraud proofs. A validator can replay data, detect an invalid state root, generate a proof showing the invalid transition, and submit that proof to the onchain verifier. The process is non-interactive: once a challenger has a proof, it can be submitted directly. That avoids the long interactive dispute games used by some optimistic systems. ## Data availability Data availability is how N1 makes the history of transaction data downloadable by network nodes. Nodes use that data to reconstruct state transitions and verify that the state is legitimate. This matters because an operator should not be able to withhold the data needed to create a fraud proof. N1 allocates data availability bandwidth on demand for apps, and that bandwidth is the main limit on app throughput. --- ## Overview(Learn) N1 splits the blockchain into a lean settlement layer and an asynchronous execution network. The result is app-level performance with shared security and data availability. ## Architecture The protocol has a few major responsibilities: - Host the network's native financial modules - an orderbook, RFQ, and a margin system, with more to come - secured by validators. - Replicate and make application data available. - Verify that app state transitions are valid or challenge invalid transitions. - Route assets and messages between settlement and app execution environments. The execution layer is made of independent processes. Each process can run a different VM and communicate with other processes through ordered channels. ## Read the protocol pages - [Modules](/learn/protocol/modules): the network's native financial modules - orderbook, RFQ, and margin system - secured by validators. - [Settlement](/learn/protocol/settlement): how N1 stores commitments, certifies state transitions, and handles bridging. - [Execution](/learn/protocol/execution): how dedicated compute environments communicate without one global state bottleneck. - [Transaction Lifecycle](/learn/protocol/transaction-lifecycle): how deposits, withdrawals, and cross-app transfers move through the system. ## Transition strategy N1 is rolling out decentralization in phases. Proton is live today with a curated operator and validator monitoring. The next phase connects to Jito (Re)staking, and the final phase moves data availability and proof verification into the N1 validator set. --- ## Modules N1's financial primitives are native modules in the network layer, not applications bolted on top. Each module is secured by validators, so it inherits the same trust and security guarantees as the chain itself. ## Why native modules On most chains, financial primitives are reassembled out of contracts on shared, general-purpose compute. That fragments liquidity, caps throughput and latency, and makes capital efficiency hard. N1 embeds these primitives in the network layer itself. Because they are native and validator-secured, the modules compose with shared security and shared liquidity rather than fragmenting it, and they run with the performance of dedicated network infrastructure. ## Orderbook A native central limit orderbook for high-throughput, low-latency matching across markets. Running it as a network module - rather than a contract on shared compute - keeps matching fast under load and concentrates liquidity in a shared primitive. ## Atomics A native way to compose multiple transactions into a single atomic bundle that either fully executes or not at all. This lets traders express advanced, complex trades - spanning multiple actions and markets - as one indivisible operation, with the guarantees enforced at the network layer. ## RFQ A native request-for-quote system for sourcing competitive quotes and executing larger or less liquid trades. As a network module it taps the same shared liquidity and validator security as the rest of the network. ## Margin system Native margining and collateral management that lets positions share risk and capital efficiently across the network. Because margining is native and validator-secured, collateral and risk are managed at the network layer instead of being rebuilt per venue. ## More to come These modules are the starting point. More native modules will follow, extending the network's financial surface area over time. Each new module inherits the same property: native to the network, secured by validators, and composing with shared security and liquidity. --- ## Settlement N1's settlement layer provides the minimum functionality required to host and secure the network's execution - including its native financial modules - and keep their data, state transitions, and bridges verifiable. ## Execution example To see what settlement guarantees, consider an execution environment running on N1 - for example the network's native financial modules. To prove honest execution for each block, the operator needs to: - Make transaction data available to users and validators. - Produce and verify a proof or receipt for correct execution. - Show that deposits and withdrawals were routed correctly. N1 provides broadcast and verification primitives for that flow. 1. **Broadcast transaction data.** The operator sends the block data to the data availability network, where it can be erasure coded and sharded across validators. 2. **Collect data availability proofs.** The network keeps the data recoverable and binds it to a commitment that other clients can fetch and verify. 3. **Execute the block.** The execution environment produces a receipt that binds the state transition to the data availability commitment. 4. **Verify and sign the receipt.** Validators check the receipt, the data commitment, and the fork choice rule before signing a state transition certificate. 5. **Submit the certificate.** The certificate is stored by the network and can be fetched by any client. Withdrawals are pushed into the operator outbox. This avoids forcing every validator into a full two-phase commit for every network operation. Most writes can be treated as unordered set replication, and only bridging needs full ordering. ## Liveness In a single-operator setting, the operator can go down or refuse to construct blocks. N1 uses the L1 as a clock and inbox for forced inclusion. Governance and monitoring can take over or force withdrawals if the operator stops serving users. The operator model is flexible: the network can begin with a single operator and later move to a decentralized operator set for stronger liveness guarantees. ## Bridging N1 supports the verification primitives required to bridge assets. Assets are bridged in with an operator-owned address and metadata that tells the network how to route them. The settlement log holds an inbox of bridged assets for the operator. Withdrawals move in the opposite direction: the operator pushes messages to an outbox that is enforced by the state transition function. ## Validator strategy during the transition Settlement security rests on validators, and the network increases how much of it does so over time. ### Today: Proton single-operator model The network today runs on Proton with a curated operator. The driver streams deposits, batches actions into blocks, and commits those blocks to the Bridge contract. Validators monitor proposed blocks and halt withdrawals if they detect a bad update. ### Next: Jito (Re)staking shared security The next phase integrates Jito (Re)staking on Solana. NCNs define how work is proven and slashed, Operators run workloads, and Vaults custody restaked SPL tokens and delegate stake. Stake activates only when the NCN, Operator, and Vault all opt in. This gives N1 modular slashable security while broadening the operator set, strengthening the trust guarantees behind the network's native financial modules. ### Future: Native N1 validators As the N1 validator set comes online, data availability sampling, proof verification, and rewards accounting move into the protocol. The network can still choose the operators and security model it needs, but the root of trust shifts from a curated setup to a broader validator set. --- ## Transaction Lifecycle The easiest way to understand the full system is to follow transactions that touch settlement, the gate app, and application execution environments. ## Deposits Deposits go through the bridge first. The bridge records the asset movement, the gate app receives the deposit event, and the gate routes the asset to the target app. ![N1 deposit lifecycle overview](/img/learn/protocol/lifecycle-deposit-overview.png) ## Withdrawals Withdrawals start in the target app. The app sends the withdrawal to the gate, and the gate forwards it to settlement during the next settlement update. ![N1 withdrawal lifecycle overview](/img/learn/protocol/lifecycle-withdrawal-overview.png) ## Cross-app transfers Cross-app transfers are composed of app-level messages. The source app sends a transfer to the gate or destination app, and the destination app credits the user once the ordered message is processed. ![N1 cross-app transfer lifecycle overview](/img/learn/protocol/lifecycle-cross-overview.png) The important property is that most work stays inside the execution network. Settlement is used for security, data availability, and bridge effects rather than every internal app action. --- ## The N1 Difference N1 is building the most capital-efficient trading venue. That means giving traders better execution, more productive capital, and globally validated security without forcing a compromise between them. ## Capital efficiency is the north star Every layer of N1 is designed around one question: how can traders execute faster and do more with their capital without compromising security? The answer rests on two foundations. ## 1. Infrastructure built for trading N1 is not a general-purpose blockchain attempting to accommodate high-frequency finance as another application. It is purpose-built for trading, with financial activity treated as a first-class network action. The objective is demanding: build decentralized, globally validated infrastructure that can deliver lower-latency execution than centralized matching engines. Reaching that standard requires solving hard systems problems across matching, data availability, scaling, and validation. N1 combines an ultra-low-latency matching engine with horizontally scalable infrastructure and validator-secured financial modules. Traders should not have to choose between execution performance and verifiable security. ## 2. Margining that makes capital work harder Execution alone does not create an efficient venue if collateral remains trapped between positions. N1 is developing margin and risk systems that allow traders to use their capital more effectively. Cross-margining lets collateral support multiple positions. Portfolio margining goes further by evaluating risk across the portfolio as a whole. Positions with offsetting exposure across correlated markets, such as BTC and ETH, may receive margin offsets when they reduce overall portfolio risk. Advanced portfolio margining is typically associated with institution-focused traditional financial venues. N1 is bringing it to onchain markets. :::note Portfolio margining availability Portfolio margining is currently in private testing and available only to select traders. Access will expand gradually as testing progresses. ::: ## The outcome Better infrastructure makes every trade more efficient. Better margining makes every unit of capital more productive. Decentralized validation keeps the venue verifiable. N1 is not pursuing incremental improvement. It is building the standard for high-performance, capital-efficient onchain finance—and the foundation for the world's financial activity to move onchain. --- ## Deposits and Withdrawals The N1 app supports deposits from Solana through the Proton bridge and from other supported chains through Unifold. ## Deposits A deposit credits a supported asset to the selected N1 trading account. If no destination account is selected, the deposit is credited to the wallet's default account. Each supported asset has a minimum deposit. Amounts below that minimum are rejected. The app displays the supported assets, networks, and applicable minimums. For deposits originating outside Solana, available chains and assets depend on the routes currently supported by Unifold in the app. ## Withdrawals A withdrawal specifies an asset, amount, source account, and destination wallet. The withdrawal fee is deducted from the requested amount. A withdrawal may be rejected if: - the account lacks the requested balance; - the amount does not cover the fee; or - the withdrawal would leave the account below its required margin. The app displays the applicable fee and net withdrawal amount before confirmation. Transfers between N1 trading accounts do not incur the external withdrawal fee. --- ## Subaccounts A wallet may have up to eight trading accounts. Each subaccount is a separate cross-margin portfolio with independent balances, positions, open orders, triggers, risk, and fee tier. ## Creating and funding a subaccount Create a subaccount and transfer supported assets to it from another account owned by the same wallet. Transfers are subject to available balance and margin requirements. They do not pay the external withdrawal fee. ## Risk and execution boundaries - Collateral in one subaccount does not support another subaccount. - Liquidation is evaluated per account. - Orders from two subaccounts owned by the same wallet are not treated as self-trades. --- ## Trading Accounts A wallet can own multiple N1 trading accounts. The first account is the default account; each additional account is an independent cross-margin subaccount. Each account maintains its own: - token balances; - perpetual positions and take-profit or stop-loss orders; - open orders; - margin state; and - fee tier. Risk is not pooled across accounts owned by the same wallet. Before trading, transferring, or withdrawing, confirm that the intended account is selected. The connected wallet controls its accounts and authorizes account actions. --- ## Definitions Risk values are denominated in USD. USDC is the quote and settlement asset. ## Position size The signed number of contracts in a perpetual futures market. A long position has a positive position size, and a short position has a negative position size. ## Fill size The signed number of contracts exchanged when orders are matched. A buy has a positive fill size, and a sell has a negative fill size. ## [Iverson bracket](https://en.wikipedia.org/wiki/Iverson_bracket) We sometimes use the Iverson bracket $\mathbb{I}[\cdot]$, where $\mathbb{I}[P] = 1$ if $P$ is true, and $0$ otherwise. This simplifies notation in various places. ## Tokens Assets such as USDC and ETH held in an account. Positive token balances can contribute collateral; negative balances represent borrows. ## Weight A number in $(0, 1]$ defining the contribution of this token to the overall account value. The weight accounts for factors such as volatility and liquidity. For example, suppose BTC has weight 0.9 and USD price 1000 USD/BTC. If a user deposits 1 BTC, then their account value increases by `0.9 * 1 BTC * 1000 USD/BTC = 900 USD`. ## Position notional (PN) The position notional is the value of a user's position. For perpetual markets, it is defined as: $$ \text{PN}_\text{perp} = |\text{position size}| \cdot p_\text{high index} $$ Because PN measures risk, it uses the upper-bound index price to conservatively maximize exposure. ## Position open notional (PON) The position open notional is the maximum position notional achievable by filling the user's open orders. It limits how much risk a user can take through open orders. For perpetual markets, it is defined as: $$ \begin{align*} \text{PON}_\text{perp} &= \max\big\{ |\text{position size} \cdot p_\text{high index} + \sum{\text{bid order size} \cdot p_\text{bid}}|, \\ &\qquad |\text{position size} - \sum{\text{ask order size}}| \cdot p_\text{high index} \big\} \end{align*} $$ Because PON measures risk, it uses the upper-bound index price to conservatively maximize exposure. ## Token value (TV) The USD value of an account's positive token balances after applying token weights and conservative index prices. TV represents the account's collateral value. ## Account value (AV) The account value is computed by summing the unrealized profit and loss (PnL) of the user's positions, token balances, and borrows. _Unrealized_ PnL has not yet been reflected in the user's balances by closing the position. PnL is _realized_ when a position is closed. To compute this, let $p_\text{open}$ be the price at which the position was opened, corresponding to its purchase or sale price. In practice, a user's position may have been opened by purchasing or selling contracts at different prices. To simplify implementation, it is sufficient to reweight the open price accordingly. For example, suppose a user purchases 5 contracts at price 20 and 10 contracts at price 30. Then: $$ p_\text{open} = \frac{5 \cdot 20 + 10 \cdot 30}{5 + 10} = 26.6(6) $$ To value positions conservatively, Nord uses the upper-bound index price for shorts and the lower-bound index price for longs. The open price must be reweighted after each fill for both makers and takers. Because position size is negative for short positions, unrealized PnL is: $$ \text{unrealized PnL} = \begin{cases} \text{position size} \cdot (p_\text{low index} - p_\text{open}) & \text{position size} \ge 0 \\ \text{position size} \cdot (p_\text{high index} - p_\text{open}) & \text{position size} < 0 \end{cases} $$ TV is the total value of the user's weighted positive token balances. Because TV represents positive collateral value, it uses the lower-bound index price. $$ \text{TV} = \sum_{\text{tokens}} \text{balance}\cdot\text{weight}\cdot\mathbb{I}[\text{balance} \ge 0] \cdot p_\text{low index} $$ Positive token balances are weighted, while negative balances are treated as borrows without a collateral weight. Account value also includes unsettled funding payments. $$ \text{AV} = \sum_{\text{markets}} \text{unrealized PnL} + \text{TV} + \sum_{\text{tokens}} \text{balance} \cdot \mathbb{I}[\text{balance} < 0] \cdot p_\text{high index} $$ Equivalently: $$ \text{AV} = \sum_{\text{markets}} \text{unrealized PnL} + \sum_{\text{tokens}} \text{balance} \cdot \begin{cases} \text{weight} \cdot p_\text{low index} & \text{balance} \ge 0 \\ p_\text{high index} & \text{balance} < 0 \end{cases} $$ AV is denominated in USD. TV corresponds to the user's collateral, whereas AV also discounts borrows and includes current PnL. This ensures that borrows are capped by collateral. ## Profit and loss (PnL) The change in a position's value. Unrealized PnL is the value change of an open position; realized PnL is reflected in the account balance when exposure is closed. ## Margin fraction (MF) Account value divided by current position notional. MF measures the health of current positions. ## Open margin fraction (OMF) The smaller of account value and token value, divided by position open notional. OMF measures account health after accounting for open orders. ## Initial margin fraction (IMF) The minimum open margin required to open a position, increase exposure, or borrow assets. ## Cancel margin fraction (CMF) The open-margin threshold at which risk-increasing open orders become eligible for cancellation. ## Maintenance margin fraction (MMF) The margin threshold below which positions and borrows become eligible for liquidation. ## Index price An index price provided by an oracle is not a single scalar value. The oracle aggregates prices for an asset and expresses the result as a median price `Pm` and confidence interval `C`, such that the actual index price falls within the `[Pm-C, Pm+C]` interval with 95% probability. For more details, see [Confidence intervals](https://docs.pyth.network/price-feeds/best-practices#confidence-intervals). Risk computations use a conservative index price: they reduce positive values and increase negative values so that account health is not overestimated. Different kinds of index price are marked as follows: - Median index price: $p_\text{index}$ - Lower-bound index price: $p_\text{low index}$ - Upper-bound index price: $p_\text{high index}$ ## Freeze If the price update condition below is true, the market is frozen. Let: - $P_{\text{prev}}$ be the previous price - $P_{\text{new}}$ be the new price - $\Delta t$ be the elapsed duration - $r_{\text{th}}$ be the threshold rate - $v$ be the confidence relaxation factor - $\Delta p = \left|P_{\text{prev}} - P_{\text{new}}\right|$ be the absolute price change - $V_{\text{prev}}$ be the previous confidence - $V_{\text{new}}$ be the new confidence - $k$ be the required confidence narrowing factor $v$ is set as: $$ v = \begin{cases} v_{\text{relax}} & \text{if } \Delta p \le V_{\text{prev}} \text{ and } V_{\text{new}} \cdot k \le V_{\text{prev}} \\ 1 & \text{otherwise} \end{cases} $$ Then freeze if: $$ \frac{\left|P_{\text{prev}} - P_{\text{new}}\right|}{P_{\text{prev}} \cdot \Delta t} > r_{\text{th}} \cdot v $$ ## Outage An oracle outage begins when no updated price has been received for one minute. An update with the same price but a different timestamp is considered a valid update. ## Mark price The market-derived price used to measure a perpetual market's premium or discount relative to its index price. ## Funding index A per-market accumulator used to calculate funding owed or received since a position's last funding settlement. ## Liquidator A permissionless actor that initiates eligible liquidation actions. ## Backstop The designated account that takes distressed positions during backstop liquidation. ## System account The protocol-controlled account that receives system fees and records deficits produced during backstop liquidation. --- ## Funding **Variables used** - $P_{\text{mark}}(t)$: mark price at time $t$. - $P_{\text{index}}(t)$: index/oracle price at time $t$. - $\mathrm{Premium}(t)$: per-sample premium. - $X$: sampling interval (seconds). - $N$: samples per hour. - $Z$: per-sample clamp magnitude. - $Y$: per-hour clamp magnitude. - $F_{\text{market}}$, $F_{\text{user}}$: market and user funding indexes. - $\text{pos\_size}$: signed position size. ## Premium sampling and TWAP - Sample interval: X seconds. - Per-sample premium is $\bigl(P_{\text{mark}}(t) / P_{\text{index}}(t)\bigr) - 1$, clamped to ±Z (e.g., set $Z \le Y$). - Over one hour, collect $N$ samples and use their average as the hourly premium TWAP. $$ \mathrm{Premium}(t) = \min\!\bigl(Z, \max(-Z,\; P_{\text{mark}}(t)/P_{\text{index}}(t) - 1)\bigr) $$ $$ \mathrm{twap}_{\mathrm{hour}} = \frac{1}{N} \sum_{k=1}^{N} \mathrm{Premium}(t_k), \quad t_k \text{ sampled every X seconds} $$ ## Funding increment and index - Funding increment for the hour is the hourly premium TWAP divided by 24 (hourly slice of a daily rate). No price multiplication needed at this level. - Apply a clamp to the hourly increment: pick a cap $Y$ (e.g., $0.01$ for 1%) and use $\Delta F_{\text{clamped}} = \mathrm{clip}(\Delta F, -Y, Y)$. Use the clamped value when updating the funding index. - Store the funding index as an accumulator per market. $$ \Delta F = \frac{1}{24} \cdot \frac{1}{N} \sum_{k=1}^{N} \mathrm{Premium}(t_k) $$ $$ F_{\text{market,new}} = F_{\text{market,old}} + P_\text{index}\cdot\mathrm{clip}(\Delta F,\; -Y,\; Y) $$ where $P_\text{index}$ is captured at funding time. ## Margin impact - Each account tracks its last funding index per market. The unrealized funding owed/receivable is based on the change in index and position size. - Variables: - $\text{pos\_size}$: signed position size (positive = long, negative = short). - $F_{\text{market}}$: current market funding index. - $F_{\text{user}}$: user's last funding index for this market. - Positive position with rising index pays; negative position receives. $$ UF = \text{pos\_size} \cdot \bigl(-(F_{\text{market,new}} - F_{\text{user}})\bigr) $$ ## Settling funding into PnL - When settling, move UF into realized PnL and advance the user's funding index to the market's. $$ RPnL_{\mathrm{new}} = RPnL_{\mathrm{old}} + UF, \quad F_{\mathrm{user,new}} = F_{\mathrm{market}} $$ ## Attempted funding manipulation - Flow of play (MM skews quotes to offload inventory): 1. Attacker leans on one side to push $P_{\text{mark}}$ away from $P_{\text{index}}$ across successive samples. $\mathrm{Premium}(t)$ moves toward +Z or -Z. 2. The MM accumulates inventory on the pressured side and skews quotes the other way to shed that risk, re-centering toward $P_{\text{index}}$. Each further push now requires the attacker to absorb more inventory at increasingly unfavorable prices as the MM skews. 3. With samples every X seconds, holding $\mathrm{Premium}(t)$ near the clamp for an hour forces the attacker to keep trading into the skewed book repeatedly, driving cumulative cost higher. 4. Per-sample clamp at ±Z limits how far any single $\mathrm{Premium}(t)$ can move; per-hour clamp at ±Y caps the funding drift. Upside is bounded while the inventory cost to maintain the skew keeps rising. 5. Net: to achieve a capped funding shift (±Y/hour), the attacker must sustain growing inventory absorption against an MM that continuously skews toward index, making a prolonged attempt increasingly expensive and likely loss-making. ## Outage If an outage accrues for 1 minute or more, funding accrued during the outage is ignored (set to zero). This is calculated per funding period. ## RFQ ### Funding sampling To find out funding premiums for RFQ markets, RFQ makers are sent fake requester orders. Fake orders are very short lived (max seconds), they look as usual RFQ orders so that RFQ makers honestly try to fill them. They never execute. A valid maker response is accepted only as a funding-price observation and does not change balances or positions. Just fake orders created periodically. #### Public sampling response Funding sampling uses the normal protobuf `FillRFQResult` and TypeScript SDK RFQ fill result. Because sampling is non-executable, protobuf omits `filled_size` and `trade_id`, and the SDK exposes both `filledSize` and `tradeId` as `null`. Real RFQ executions use the same result shape with both values set. ### Funding formalization For funding, Nord should run a separate sampler. Every `X` seconds, the sampler sends a standardized request-to-fill to subscribed RFQ makers for the configured impact size. Nord may accept a valid response as a non-executable observation, but never executes the probe as a trade. The response is used only to observe the impact price. The sampler should collect both bid-side and ask-side impact prices and derive `pmid` from them: $$ P_{\text{mid}} = \frac{P_{\text{bid impact}} + P_{\text{ask impact}}}{2} $$ For funding tick $k$ and side $d$, let $F_{d,k}$ be the funding-participating RFQ makers that return fresh, valid, non-executable impact proposals: $$ F_{d,k} = \{i \mid B_{i,m} = 1 \text{ and } a_{d,k,i} \le A_{\text{max}} \text{ and } V_{d,k,i} = 1\} $$ For each side, aggregate the valid impact proposals into an impact price: the bid impact is the highest valid bid proposal, and the ask impact is the lowest valid ask proposal. $$ P_{\text{bid impact}, k} = \max(\{P_{\text{bid}, k, i} \mid i \in F_{\text{bid}, k}\}) $$ $$ P_{\text{ask impact}, k} = \min(\{P_{\text{ask}, k, i} \mid i \in F_{\text{ask}, k}\}) $$ $$ P_{\text{mid}, k} = \frac{P_{\text{bid impact}, k} + P_{\text{ask impact}, k}}{2} $$ Funding premium is then computed against the index: $$ \text{Premium}_k = \frac{P_{\text{mid}, k}}{P_{\text{index}, k}} - 1 $$ If either side has no valid proposal, the tick has no $P_{\text{mid}}$ sample: $$ |F_{\text{bid}, k}| = 0 \text{ or } |F_{\text{ask}, k}| = 0 \Rightarrow S_k = \emptyset $$ Funding edge cases: - if only one side is returned, the sample is missing for $P_{\text{mid}}$; - if the index price is unavailable, ignore the sample or use the existing outage policy; --- ## Overview(Trading) Nord provides perpetual futures (over CLOB and RFQ) and spot trading (over RFQ) in a single cross-collateralized account. Its margining system limits the risk an account can take. Risk is denominated in USD, with settlement in USDC. ## Cross-collateralized accounts Positive token balances provide collateral across the account's markets. Collateral weights account for differences in asset volatility and liquidity. Nord evaluates this collateral together with borrows, perpetual positions, profit and loss, funding, and open orders. For example, suppose an account has 20,000 USDC of collateral and opens positions in two perpetual markets: $$ \text{collateral} = 20{,}000\ \text{USDC} $$ The BTC market has a maximum leverage of 10x, so a 100,000 USD BTC position requires: $$ \frac{1}{10} \cdot 100{,}000 = 10{,}000\ \text{USDC} $$ The ETH market has a maximum leverage of 5x, so a 25,000 USD ETH position requires: $$ \frac{1}{5} \cdot 25{,}000 = 5{,}000\ \text{USDC} $$ Both positions draw from the same USDC collateral: $$ \begin{aligned} \text{total initial margin} &= 10{,}000 + 5{,}000 = 15{,}000\ \text{USDC} \\ \text{remaining margin} &= 20{,}000 - 15{,}000 = 5{,}000\ \text{USDC} \end{aligned} $$ The 20,000 USDC is not assigned to either position separately. If the BTC position loses 3,000 USD, account value falls to 17,000 USD and the margin remaining across both markets falls to $17{,}000 - 15{,}000 = 2{,}000$ USDC. ## Account health Account health compares the conservative value of an account's collateral and positions with its current and potential exposure. Open orders are included because filling them may increase the account's exposure. Nord uses oracle confidence bounds to avoid overstating collateral or understating risk. See [Definitions](/trading/margining/definitions) for account value, position notional, position open notional, and index price calculations. ## Margin lifecycle When a user opens a position, increases exposure, or borrows assets, the account must satisfy its initial margin requirement. Afterward, its margin fraction determines which actions are allowed: - Initial margin controls new exposure. - Cancel margin determines when risk-increasing orders can be removed. - Maintenance margin determines when positions and borrows become eligible for liquidation. See [Margin](/trading/margining/margin) for margin formulas, thresholds, and price bands. ## Liquidation Overleveraged accounts are liquidated. Risk-increasing orders are canceled and positions are reduced, and possibly closed, until the account returns to a healthy margin level. More distressed accounts may have their positions transferred to the backstop. Liquidated users are charged a liquidation fee. See [Liquidations](/trading/margining/liquidations) for liquidation eligibility, execution, and backstop behavior. ## Funding Perpetual funding keeps market pricing aligned with the index price. Accrued funding affects account value and is settled into PnL. See [Funding](/trading/margining/funding) for premium sampling, funding indexes, settlement, and outage behavior. --- ## Liquidations ## Overview The goal of this document is to specify the high-level design of liquidation execution. For risk specifics, see [Margin](./margin). Liquidation consists of several phases, which are enabled based on the account's health. All actions are initiated by a permissionless liquidator operating through a regular user account. The liquidator does not need to post collateral. ## Phases The first phase consists of per-market or per-balance activity on behalf of the user. Order cancellation, position reduction, and trading an existing token for a token owed to the system are typical actions. As long as the user is unhealthy and there are actions that can improve health per market, they are executed. The next phase is account-wide. The backstop takes the user's positions and some amount of balance, up to the full balance, at prices near the index price. ## Liquidation search strategy The liquidator and engine state may be described as a distributed state machine running in a loop, potentially until bankruptcy. Here is the general flow of liquidation states: ```mermaid stateDiagram-v2 state can_cancel <> state can_reduce_perp_position <> state can_sell <> [*] --> can_cancel CancelOrders --> can_cancel can_cancel --> CancelOrders: Can cancel can_cancel --> can_reduce_perp_position : Cannot cancel ReducePerpPosition --> can_cancel can_reduce_perp_position --> ReducePerpPosition : Can reduce negative PnL perpetual can_reduce_perp_position --> can_sell : Cannot reduce negative PnL can_sell --> SellOrCollectPositivePnL: Can sell or collect positive PnL SellOrCollectPositivePnL --> can_cancel can_sell --> TakeAllOrBankruptcy: Cannot sell or collect positive PnL TakeAllOrBankruptcy --> [*] ``` The priority of a borrow or perp position is based on its notional value: the value of the borrow for spot positions or the value of the position for perps. The actions and choices available during liquidation are outlined in [Margin](./margin). So, in general, the next steps are: ### 1. Reduce or cancel outstanding orders The liquidator closes all orders that would increase the user's position and make the account less healthy if filled. This ensures the user will not increase the risk of their positions through newly settled orders. Orders outside the price band are cancelled. ### 2. Reduce perp positions and borrows Per-market perp reduction is executed by a liquidation action that does not specify a size. The engine derives the reducing side from the liquidated account's current position, computes the ideal reduction size from the current risk state, position, market margin parameters, and conservative index price, then executes an internal, immediate, reduce-only order. Orderbook depth does not determine the ideal size. It only constrains how much of the internal order can actually fill. The liquidator selects the target account and market, while the engine computes the ideal reduction size. $$ \begin{aligned} \text{deficit} &= \text{IMF\_numer}_{now} - \text{OMF\_numer}_{now} \\ \text{RO\_quote\_size} &= \frac{\max(0, \text{deficit})}{\text{IMF}_{base,m}} \end{aligned} $$ Here, `RO_quote_size` is the quote notional to reduce. It is converted to base size using the same reference price used for the selected market's risk notional and rounded up. ### 3. Not enough balances to reduce $$ \Delta \text{Price} = P_\text{sell} - P_\text{index} $$ If $\Delta \text{Price} > 0$, token trading is allowed unless the token being sold is not owed to the market. Otherwise: $$ \begin{aligned} \Delta \text{Ratio} &= \frac{\Delta \text{Price}}{P_\text{index}} \\ \Delta \text{Size} &= \left(\frac{\text{Size}}{\text{Depth} + 1}\right)^2 \end{aligned} $$ **Not enough token to pay debt** Selling is allowed if: $$ \frac{\Delta \text{Ratio}}{1 - \Delta \text{Size}} < \text{slippage} $$ where $\text{slippage} = \frac{1}{20}$. The token is sold for USDC, which is then used to buy the token owed. **Not enough token to close perp position** If there is not enough USDC to close the position, tokens with no outstanding debt may be sold for USDC. The same sell-decision formula is used. **Details** The liquidator sorts tokens to check the smallest $\Delta \text{Size}$ first, but still validates that the sell-decision formula holds. This also allows a low-collateral liquidator to improve health, or bankrupt the account, by market-trading the position. ### 4. Take all positions below 2/3 MMF when not bankrupt 1. Validate eligibility and select transfer scope = all positions. 2. Cancel all open orders for the liquidated account. 3. Settle funding for the liquidated account and the backstop account. 4. Compute transfer prices for each position using index bounds and exchange-favoring rounding. - With `favor_exchange = true`, a long uses the low floor (lower mantissa), and a short uses the high ceiling (higher mantissa). - With `favor_exchange = false`, a long uses the high ceiling (higher mantissa), and a short uses the low floor (lower mantissa). 5. Apply closing fees before transfer: - `notional = abs(size) * transfer_price` - `fee = ceil((trading_fee_bps + 1 bp) * notional)` - Split fee between system and backstop. 6. Transfer positions to the backstop at the transfer price: - Liquidated account realizes price PnL at the transfer price. - The backstop's unrealized PnL starts at zero for the transferred size. 7. If the account cannot pay debt after fees and price PnL, fall through to deficit absorption: - The backstop takes the minimum positive token balances needed to cover the remaining negative quote balance. - Token conversion uses a conservative token value: the weighted low index price. - Conversion stops once the user's quote balance becomes non-negative or no positive token balances remain. 8. Leave remaining balances in the user account. 9. Emit a transfer notification with per-market details and fee split. ### Bankruptcy All assets are moved from the liquidated account to the backstop account at index prices to cover the liquidated account's negative AV. The liquidated account retains zero balances, and the negative quote balance is applied to the system account. ## Liquidation score The liquidator uses `liquidation_score` to prioritize accounts. Higher scores are checked first. | Account state | `liquidation_score` | | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `NoPosition` | not tracked | | `Bankrupt` | $2 + \text{negative account value} - \text{positive account value}$ | | `Active`, $\text{PN} > 0$, $\text{MF} \le \text{MMF}$ | $1 + \left(\frac{\sum \text{PON} \cdot \text{MMF}_\text{base}}{\sum \text{PON}} - \frac{\text{AV}}{\sum \text{PN}}\right)$ | | `Active`, $\text{PN} = 0$, $\text{PON} > 0$, $\text{OMF} \le \text{CMF}$ | $\frac{\sum \text{PON} \cdot \text{CMF}_\text{base}}{\sum \text{PON}} - \frac{\min\{\text{AV}, \text{TV}\}}{\sum \text{PON}}$ | | `Active`, $\text{PN} > 0$, $\text{MF} > \text{MMF}$ | $\frac{\left(\frac{\sum \text{PON} \cdot \text{MMF}_\text{base}}{\sum \text{PON}} - \frac{\text{AV}}{\sum \text{PN}}\right)}{\sum \text{PN}}$ | | `Active`, $\text{PN} = 0$, $\text{PON} > 0$, $\text{OMF} > \text{CMF}$ | $\frac{\left(\frac{\sum \text{PON} \cdot \text{CMF}_\text{base}}{\sum \text{PON}} - \frac{\min\{\text{AV}, \text{TV}\}}{\sum \text{PON}}\right)}{\sum \text{PON}}$ | The score bands keep bankruptcy above position liquidation, and position liquidation above open-order cancellation. ## Backstop liquidation specification ### 1. Overview and principles This section specifies the deterministic logic for backstop liquidation in the Nord engine. This process transfers distressed positions from a user to the system backstop. #### Core invariants 1. **Zero-sum value transfer (excluding system fees):** The sum of the user and backstop account values (`balance + base_size * (index - open_price)`) changes only by the fees taken by the system. Including the system account, the net change is zero. 2. **Conservative valuation:** Backstop position merging uses rounding that favors the system (round up for longs, round down for shorts). 3. **Non-negative prices:** Prices are `u64`. Merge logic follows `apply_fill` semantics and never stores a negative price. ### 2. Data types and definitions | Variable | Type | Notes | | :--- | :--- | :--- | | `balance` | `i64` | Signed quote token balance (USDC). | | `base_size` | `i64` | Signed position size (positive for long, negative for short). | | `open_price` | `u64` | Entry price (always positive). | ### 3. Algorithm details #### Step A. Funding settlement Before transfer, funding is settled to mark positions to the current index. If the backstop merge is implemented via `apply_fill`, backstop funding is already handled there, so only user settlement is strictly required. - `pnl = position.base_size * (position.funding_index - market.funding_index)` - `account.balance += pnl` - `position.funding_index = market.funding_index` #### Step B. Transfer price The transfer occurs at the index mantissa that favors backstop liquidity and is least favorable to the user. - If `user_pos.base_size > 0` (long): `transfer_price = index.mantissa_low` - If `user_pos.base_size < 0` (short): `transfer_price = index.mantissa_high` #### Step C. Exchange fees Fees are calculated as if the user closed the position at `transfer_price`. One basis point of the fees is paid to the backstop. - `notional = abs(base_size) * transfer_price` - `total_fee = ceil(abs(base_size) * transfer_price * fee_rate)` - `backstop_fee = min(1 bps of notional, total_fee)` - `system_fee = total_fee - backstop_fee` - Updates: - `user.balance -= total_fee` - `system.balance += system_fee` - `backstop.balance += backstop_fee` #### Step D. Position transfer The user closes the position at `transfer_price`, and the position is transferred to the backstop in the same direction. PnL is realized at the transfer price; there is no direct balance transfer between the user and backstop. - `user_pnl = base_size * (transfer_price - open_price)` - Updates: - `user.balance += user_pnl` - `user.positions.remove(id)` #### Step E. Backstop merge logic The backstop now has an incoming position piece: `(incoming_size, transfer_price)`, where `incoming_size = user_pos.base_size` because the position is transferred, not traded. It is merged into the existing backstop position: `(old_size, old_price)`. This merge is equivalent to applying a fill of `incoming_size` at `transfer_price` to the backstop position, using `PerpPosition::apply_fill` semantics. **1. Zeroed out (exact close)** - Condition: `new_size == 0` - Logic: The entire position is closed at `transfer_price`. - PnL: - `closed_size = abs(old_size)` - `backstop.balance += closed_size * sign(old_size) * (transfer_price - old_price)` - State: Remove the position. **2. Increase (same sign)** - Condition: `sign(old_size) == sign(incoming_size)` - Logic: Use the weighted-average price over the total size. - Price: - `new_size = old_size + incoming_size` - `new_price = (abs(old_size) * old_price + abs(incoming_size) * transfer_price) / abs(new_size)` - Balance: No change. **3. Reduce (opposite sign, no flip)** - Condition: `sign(old_size) != sign(incoming_size)` and `sign(new_size) == sign(old_size)` - Logic: Realize PnL on the closed portion at `transfer_price`. - PnL: - `closed_size = min(abs(old_size), abs(incoming_size))` - `backstop.balance += closed_size * sign(old_size) * (transfer_price - old_price)` - Price: `new_price = old_price`. **4. Flip (opposite sign)** - Condition: `sign(old_size) != sign(incoming_size)` and `sign(new_size) != sign(old_size)` - Logic: Realize PnL on the closed portion at `transfer_price`. - PnL: - `closed_size = abs(old_size)` - `backstop.balance += closed_size * sign(old_size) * (transfer_price - old_price)` - Price: `new_price = transfer_price`. #### Step F. Absorb negative user balance If the user ends with a negative balance, the system absorbs it. - If `user.balance < 0`: - `system.balance += user.balance` - `user.balance = 0` This is a pure transfer between the user and system and does not change total value. ### 4. Invariant validation (pre-absorption) This validation is a proof sketch only and is not a runtime step. It is evaluated after Step E and before loss absorption. For this proof sketch only, use `index` everywhere (so `transfer_price = index`) and ignore rounding and `mantissa_low`/`mantissa_high` selection. Assumptions: $$ \text{account\_value} = \text{balance} + \text{base\_size} \cdot (\text{index} - \text{open\_price}) $$ Target equalities: $$ \begin{aligned} \text{user\_ac\_value\_before} - \text{system\_fee} - \text{backstop\_fee} &= \text{user\_ac\_value\_after} \\ \text{backstop\_ac\_value\_before} + \text{backstop\_fee} &= \text{backstop\_ac\_value\_after} \\ \text{system\_balance\_before} + \text{system\_fee} &= \text{system\_balance\_after} \end{aligned} $$ Start state: $$ \begin{aligned} \text{user\_ac\_value\_before} &= \text{user.balance} + \text{user.size} \cdot (\text{index} - \text{user.entry\_price}) \\ \text{backstop\_ac\_value\_before} &= \text{backstop.balance} + \text{backstop.size} \cdot (\text{index} - \text{backstop.entry\_price}) \\ \text{system\_balance\_before} &= \text{system.balance} \end{aligned} $$ After fees and transfer (Step C + Step D): $$ \begin{aligned} \text{user\_ac\_value\_after} &= \text{user.balance} - \text{system\_fee} - \text{backstop\_fee} + \text{user.size} \cdot (\text{index} - \text{user.entry\_price}) \\ \text{system\_balance\_after} &= \text{system.balance} + \text{system\_fee} \end{aligned} $$ Backstop merge proof by cases (Step E): Case 1: same-sign increase $$ \begin{aligned} \text{new\_entry\_price} &= \frac{\text{backstop.size} \cdot \text{backstop.entry\_price} + \text{user.size} \cdot \text{index}}{\text{backstop.size} + \text{user.size}} \\ \text{new\_size} &= \text{backstop.size} + \text{user.size} \\ \text{backstop\_ac\_value\_after} &= \text{backstop.balance} + \text{backstop\_fee} + (\text{backstop.size} + \text{user.size}) \cdot (\text{index} - \text{new\_entry\_price}) \\ &= \text{backstop.balance} + \text{backstop\_fee} + \text{backstop.size} \cdot (\text{index} - \text{backstop.entry\_price}) \\ &= \text{backstop\_ac\_value\_before} + \text{backstop\_fee} \end{aligned} $$ Case 2: opposite-sign reduce (no flip) $$ \begin{aligned} \text{closed\_size} &= \min(\lvert\text{backstop.size}\rvert, \lvert\text{user.size}\rvert) \\ \text{new\_size} &= \text{backstop.size} + \text{user.size} \\ \text{backstop.balance} &\mathrel{+}= \text{closed\_size} \cdot \operatorname{sign}(\text{backstop.size}) \cdot (\text{index} - \text{backstop.entry\_price}) \\ \text{backstop\_ac\_value\_after} &= \text{backstop.balance} + \text{backstop\_fee} + \text{new\_size} \cdot (\text{index} - \text{backstop.entry\_price}) \\ &= \text{backstop\_ac\_value\_before} + \text{backstop\_fee} \end{aligned} $$ Case 3: opposite-sign exact close (new\_size = 0) $$ \begin{aligned} \text{closed\_size} &= \lvert\text{backstop.size}\rvert \\ \text{backstop.balance} &\mathrel{+}= \text{closed\_size} \cdot \operatorname{sign}(\text{backstop.size}) \cdot (\text{index} - \text{backstop.entry\_price}) \\ \text{backstop\_ac\_value\_after} &= \text{backstop.balance} + \text{backstop\_fee} \\ &= \text{backstop\_ac\_value\_before} + \text{backstop\_fee} \end{aligned} $$ Case 4: opposite-sign flip $$ \begin{aligned} \text{closed\_size} &= \lvert\text{backstop.size}\rvert \\ \text{new\_size} &= \text{backstop.size} + \text{user.size} \\ \text{backstop.balance} &\mathrel{+}= \text{closed\_size} \cdot \operatorname{sign}(\text{backstop.size}) \cdot (\text{index} - \text{backstop.entry\_price}) \\ \text{backstop\_ac\_value\_after} &= \text{backstop.balance} + \text{backstop\_fee} + \text{new\_size} \cdot (\text{index} - \text{index}) \\ &= \text{backstop.balance} + \text{backstop\_fee} + \text{backstop.size} \cdot (\text{index} - \text{backstop.entry\_price}) \\ &= \text{backstop\_ac\_value\_before} + \text{backstop\_fee} \end{aligned} $$ ### 5. Mermaid flowchart ```mermaid flowchart TD Start([Start Backstop Liquidation]) --> Funding[Step A: Settle Funding
User; Backstop optional if apply_fill] Funding --> TransferPrice[Step B: Transfer Price
mantissa_low or mantissa_high] TransferPrice --> Fees[Step C: Fees
User -> System/Backstop] Fees --> Transfer[Step D: Transfer Position
User realizes PnL
Backstop receives same side] Transfer --> Merge[Step E: Merge Backstop Position] Merge --> SameSign{"sign old_size == sign incoming_size?"} SameSign -- Yes (Increase) --> Increase[Weighted avg price
Conservative rounding] SameSign -- No --> NewSizeZero{"new_size == 0?"} NewSizeZero -- Yes (Exact Close) --> Close[Realize PnL
Remove Position] NewSizeZero -- No --> Flip{"sign new_size != sign old_size?"} Flip -- Yes (Flip) --> FlipPrice[Realize PnL
Set price = transfer_price] Flip -- No (Reduce) --> Reduce[Realize PnL
Keep price = old_price] Increase --> Deficit{"User balance < 0?"} Close --> Deficit FlipPrice --> Deficit Reduce --> Deficit Deficit -- Yes --> Absorb[Step F: System absorbs deficit
User balance = 0] Deficit -- No --> End([Finish]) Absorb --> End ``` --- ## Margin The margin fractions define the leverage permitted by Nord. Each market has different base fractions based on factors such as the risk and volatility of the underlying asset. ## Margin fractions ### Margin fraction $\text{MF}$ Calculated per user as: $$ \text{MF}_{user} = \frac{\text{AV}}{\sum_{markets} \text{PN}} $$ ### Open margin fraction $\text{OMF}$ $$ \text{OMF}_{user} = \frac{\min\big\{\text{AV}, \text{TV} \big\}}{\sum_{markets} \text{PON}} $$ OMF is the ratio between the account's available collateral value and its possible exposure after open orders fill. The numerator includes unsettled funding. ### Initial margin fraction $\text{IMF}$ Defines the initial margin to open a position or borrow assets. After opening a position or borrowing an asset, we must have $\text{OMF}_user > \text{IMF}_user$. First, define a per-market or per-asset **base IMF**. $$ \begin{align*} \text{IMF}_\text{base} &= \frac{1}{\text{max leverage}} & (\text{perp}) \\ \text{IMF}_\text{base} &= \frac{1.1}{\text{weight}_\text{base token}} - 1 & (\text{spot}) \end{align*} $$ where - $\text{max leverage}$ is the maximum leverage multiplier for a specific market. The implementation may operate with the market's $\text{imf}$ value, in which case: $$ \begin{align*} \text{IMF}_\text{base} &= {\text{imf}_\text{market}} & (\text{perp}) \end{align*} $$ - $\text{weight}$ is the weight of the spot market's base token. Note that in practice, we need only store the market IMF and token IMF. The leverage is implied from this value. Then we have the user's IMF. $$ \text{IMF}_{user} = \frac{\sum \text{PON} \cdot \text{IMF}_\text{base}}{\sum \text{PON}} $$ Note that for tokens, $\text{IMF}_\text{base}$ is only defined if the token is borrowable. Because OMF and IMF have the same denominator, the condition $\text{OMF} > \text{IMF}$ can be simplified to: $$ \min\big\{\text{AV}, \text{TV} \big\} > \sum \text{PON} \cdot \text{IMF}_\text{base} $$ Let us use $\text{OMF\_numer} = \min\{\text{AV}, \text{TV}\}$. ### Cancel margin fraction $CMF$ The $OMF$ below which open orders that extend the user's current position are cancelled. Defined similarly to IMF. $$ \begin{align*} \text{CMF}_\text{base} &= \frac{5}{8} \text{IMF}_\text{base} & (\text{perp}) \\ \text{CMF}_\text{base} &= \text{IMF}_\text{base} & (\text{spot}) \end{align*} $$ $$ \text{CMF}_{user} = \frac{\sum \text{PON} \cdot \text{CMF}_\text{base}}{\sum \text{PON}} $$ ### Maintenance margin fraction $MMF$ The MF threshold below which the user's positions and borrows become eligible for liquidation. $$ \begin{align*} \text{MMF}_\text{base} &= \frac{1}{2} \text{IMF}_\text{base} & (\text{perp}) \\ \text{MMF}_\text{base} &= \frac{1.03}{\text{weight}} - 1 & (\text{spot}) \end{align*} $$ $$ \text{MMF}_{user} = \frac{\sum \text{PON} \cdot \text{MMF}_\text{base}}{\sum \text{PON}} $$ ## Invariants If a user can perform a financial operation by executing two separate actions sequentially, then the equivalent financial operation performed as a single action must also succeed. For example, if a user can close a position and then open an opposite position, they must also be able to perform this as a single, atomic operation. ## Price bands Nord rejects perpetual order requests with a limit price outside a configured band around the index price. With band $B$, the lowest permitted price is $p_\text{low index} \cdot (1 - B)$ and the highest permitted price is $p_\text{high index} \cdot (1 + B)$. Currently, $B = 0.2$. --- ## Contract Specifications Contract specifications vary by market. Refer to the N1 app for current values before placing an order. | Field | Description | | --- | --- | | Market | Underlying asset represented by the contract | | Quote asset | Asset in which prices, PnL, funding, and fees are denominated | | Tick size | Smallest permitted price increment | | Size increment | Smallest permitted order-size increment | | IMF | Initial margin ratio applied when risk increases | | CMF | Cancellation margin ratio used to restrict withdrawals and remove risk-increasing orders | | MMF | Maintenance margin ratio below which a position is eligible for liquidation | | Execution mode | CLOB or RFQ | | Operating state | Normal, post-only, trade-only, frozen, or not ready | Orders must conform to the market's tick size and size increment. The app also displays available market data, including index price, mark price, projected funding, next funding time, open interest, and 24-hour price and volume statistics. --- ## Execution Modes Each perpetual market uses one execution mode: central limit order book (CLOB) or request for quote (RFQ). The two modes are not active concurrently for the same market. ## CLOB CLOB markets match incoming orders against resting liquidity using price-time priority. Traders may submit limit, post-only, immediate-or-cancel, and fill-or-kill orders. See [Order Book and Matching](../orders/order-book-and-matching) and [Order Types](../orders/order-types) for execution details. CLOB markets normally operate in the **Normal** regime, where all supported order types are accepted. In exceptional circumstances, a market may be placed in a more restrictive regime: - **Post-only:** only post-only orders may be added. - **Trade-only:** immediate orders may consume existing liquidity, but no new resting orders may be placed. ## RFQ RFQ markets execute against designated market makers rather than resting order-book liquidity. 1. The requester specifies the market, side, base size, price bound, and optional timeout. 2. RFQ makers may return a firm price and, when permitted, compatible fill-size bounds. 3. The request and returned quote must both remain within their validity periods. 4. A request fills in full by default, or within the requester's size bounds when partial fills are enabled. The requester's price is a private execution bound and is not shown to responding makers. The maker's price must satisfy that bound. RFQ requests reserve account margin while open. They may be canceled and expire automatically. An executed RFQ updates the trader's position, PnL, funding, and fees in the same way as other perpetual trades. See [RFQ Markets](./rfq-markets) for the complete request, response, partial-fill, timeout, and pricing flow. --- ## Oracle, Index, and Mark Prices N1 uses separate index and mark prices because they serve different purposes. ## Index price The index price is an external reference price for the underlying asset. Index price is used for: - collateral and margin calculations; - take-profit and stop-loss activation; - funding calculations; - price-band validation; and - liquidations. If a reliable index price is unavailable, trading may be restricted until valid pricing resumes. ## Mark price The mark price reflects the market's traded pricing relative to the index. For CLOB markets, it is derived from the best available bid and ask. The mark price is used for displayed unrealized trading PnL and for the premium component of funding. It does not trigger take-profit or stop-loss orders. For RFQ markets, pricing from eligible market makers is used to measure the market premium for funding. --- ## Perpetual Markets Perpetual contracts provide directional exposure to an underlying index without an expiry date. Positions are margined and settled in the market's quote token. A buy increases long exposure or reduces short exposure. A sell increases short exposure or reduces long exposure. If an order is larger than the existing opposite position, the remaining size opens a position in the new direction. ## Market state Each market defines: - an underlying asset and quote asset; - tick size and order-size increment; - initial, cancellation, and maintenance margin ratios; - an execution mode: CLOB or RFQ; and - an operating state. A frozen or not-ready market does not accept normal trading. These states are reserved for exceptional circumstances. --- ## RFQ Markets Request for quote (RFQ) markets execute against competing market makers instead of a public order book. They are designed for markets where a trader benefits from requesting a firm price for a specific size rather than matching against visible resting orders. :::note Availability RFQ support is available on devnet and mainnet in v20. ::: ## RFQ compared with CLOB | | CLOB market | RFQ market | | --- | --- | --- | | Liquidity | Visible resting orders | Responses from RFQ makers | | Trader price protection | Limit price on the order | Private execution bound | | Execution | Price-time matching | First valid maker response for the permitted size | | Lifetime | Determined by order type and cancellation | Short request timeout, cancellation, or execution | | Partial fills | Determined by available order-book liquidity | Full fill by default; optional when the requester permits it | A market operates in either CLOB or RFQ mode. Both modes are not active at the same time for the same market. ## How an RFQ executes ### 1. The trader creates a request The requester specifies: - the market and side; - the total base size; - a private price bound; - how long the request remains valid; - an optional minimum fill size; and - whether the request is reduce-only. The price bound defines the worst price the requester will accept. It is validated against the market's current index-price band but is not included in the request delivered to makers. Before opening the request, N1 checks that the market is operational and in RFQ mode, the size and price are valid, and the account has sufficient margin. An open RFQ counts toward the account's open orders and margin requirements. ### 2. Makers receive the request RFQ makers receive the order ID, market, side, size, and remaining lifetime. They do not receive the requester's private price bound. The delivery feed is a notification layer. The request itself lives in engine state, so a delayed or disconnected maker feed does not cancel it. The request remains open until it executes, is canceled, or expires according to engine time. ### 3. A maker submits a fill An authorized RFQ maker responds with a firm execution price. If partial fills are allowed, the maker may also provide a compatible minimum and maximum fill size. N1 accepts a response only when: - the market is operational and remains in RFQ mode; - both the request and maker response are still valid; - the maker is currently authorized; - the execution price satisfies the requester's private bound and the market price band; - the fill size satisfies both parties' size rules; and - the trade passes margin, health, and reduce-only checks for both accounts. The first valid response executes for the permitted size. Other responses do not execute against size that has already been filled. ### 4. N1 settles the trade Execution and settlement happen together. N1 updates both accounts' positions, realized PnL, funding, quote balance, and RFQ maker or taker fees. If any required check fails, the attempted fill does not commit partial account changes. The execution appears in account activity and market trade history like other perpetual trades. ## Full and partial fills A standard RFQ without partial-fill parameters is all-or-none for its full size. When the requester sets a minimum fill size, a maker may fill an amount at or above that minimum and no more than the open size. If the fill leaves a valid remainder, that remainder stays open under the same order ID, price bound, and expiry, and another maker may fill it later. The request closes when it is fully filled or when the remaining size is smaller than the requester's minimum. Reduce-only requests may be capped or partially filled to avoid increasing or flipping the position, and may be resized or removed when the underlying position changes. ## Timeouts and cancellation Both the request and the maker response have validity windows. N1 checks timeouts against engine time when a response arrives, even if an expired request has not yet been removed from the feed. The requester may cancel an open RFQ. Otherwise, it is removed automatically after expiry. A late maker response fails without changing positions or balances. ## Market prices and funding RFQ markets do not have public order-book depth from which to derive an impact price. N1 instead sends designated makers short-lived, non-executable sampling requests for standardized bid and ask sizes. Maker responses to these samples never create trades or change account state. N1 uses the highest valid bid and lowest valid ask to form the sample midpoint. If either side has no valid response, that round does not produce a midpoint sample. These samples contribute to RFQ market pricing and funding calculations. See [Funding](/trading/margining/funding) and [Oracle, Index, and Mark Prices](/trading/markets/oracle-index-and-mark-prices) for the broader pricing model. --- ## Fees Trading fees are assessed per fill according to the account's fee tier, the market type, the market's execution mode, and the account's role in the trade. - **Maker:** the resting order. - **Taker:** the incoming order. On RFQ markets, the quote provider is the maker and the requester is the taker. Proton v20 can configure separate CLOB and RFQ maker and taker rates for the same perpetual fee tier. Clients should display the rate returned for the account and execution mode instead of assuming one rate applies to both modes. For perpetuals, the trading fee is the fill notional multiplied by the applicable fee rate. Fees are charged in the quote asset. ## Fee-tier qualification Fee tiers are evaluated daily using maker activity from the last 14 completed UTC days. The current, incomplete UTC day is not included. Maker volume is aggregated across the trading accounts owned by the same wallet. Taker volume does not count toward standard fee-tier qualification. Traders qualify through either: - wallet-wide maker volume; or - for T2 through T4, the wallet's share of total platform maker volume over the same period. ## Mainnet CLOB fee schedule The following CLOB rates reflect the live mainnet Nord configuration as of August 15, 2026. RFQ rates may differ by tier. | Tier | Wallet-wide maker volume | Alternative maker share | Maker fee | Taker fee | | --- | ---: | ---: | ---: | ---: | | T1 | ≤ \$5M | — | 0.01% | 0.035% | | T2 | > \$5M | ≥ 0.5% | 0.005% | 0.03% | | T3 | > \$25M | ≥ 2.5% | 0% | 0.025% | | T4 | > \$100M | ≥ 10% | 0% | 0.023% | | T5 | > \$1B | — | 0% | 0.02% | The N1 app displays the tier and fee rates currently applied to an account. Traders who qualify for a higher tier on another venue may apply for Fee Tier Matching from the Fee Tier screen. ## Withdrawal fees Under normal conditions, an external withdrawal incurs a fee equal to 1 USDC. For withdrawals in another asset, the equivalent amount is deducted from that asset using its current index price. Transfers between N1 trading accounts do not incur this fee. The amount to be received is displayed before a withdrawal is confirmed. --- ## Order Book and Matching CLOB markets use price-time priority. Incoming bids match the lowest available asks at or below the bid limit. Incoming asks match the highest available bids at or above the ask limit. Matching proceeds from best price to worse prices until an order's price, base-size, or quote-size constraint is reached. Within one price level, resting orders execute first in, first out. ## Maker and taker - A **maker** is the resting order already on the book. - A **taker** is the incoming order that consumes resting liquidity. A limit order may act as taker for its immediately executable portion and maker for any remainder that posts. ## Order constraints Orders may be specified by asset quantity or quote notional. Execution stops when the selected amount has been filled or the order's price constraint can no longer be satisfied. Each account may have at most 100 open orders. Orders are also subject to available margin, position limits, market state, and permitted price bands. --- ## Order Types N1 supports four CLOB order types. | Type | Execution | | --- | --- | | Limit | Trades at the limit price or better, then posts any remaining quantity | | Post-only | Posts only if it would not execute immediately; otherwise the entire action is rejected | | Immediate-or-cancel (IOC) | Executes immediately up to the order constraints and cancels the remainder | | Fill-or-kill (FOK) | Fills the entire requested amount immediately or cancels without a partial fill | IOC and FOK orders do not rest on the book. An immediate order without a price limit executes against available liquidity and remains limited by its specified amount. ## Reduce-only Reduce-only may be applied to a perpetual order. The order must be opposite the current position and cannot increase exposure or flip the position. Reduce-only execution is capped at the current position size. If the position changes, open reduce-only orders may be resized or canceled to prevent them from exceeding the remaining position. --- ## Self-Trade Prevention Self-trade prevention can be enabled on a CLOB order. When an incoming order would match a resting order from the same account, the resting maker order is canceled instead of executing the self-trade. The incoming order then continues matching against other eligible liquidity. Self-trade prevention is optional and is scoped to a trading account: - orders from the same account are treated as self-liquidity; - orders from different subaccounts are not, even when the same wallet owns both accounts. If self-trade prevention is not enabled, orders from the same account may match. --- ## Take Profit and Stop Loss (TP/SL) Take-profit and stop-loss orders reduce or close an existing perpetual position. They cannot open a new position or increase an existing one. Triggers activate from the **index price**, not the mark price or last trade: | Position | Stop loss | Take profit | | --- | --- | --- | | Long | Index at or below trigger | Index at or above trigger | | Short | Index at or above trigger | Index at or below trigger | The trigger price must not already be satisfied when the trigger is added. ## Execution When activated, the order attempts to reduce the position immediately. A limit price and order size may be specified. If no size is supplied, it attempts to close the full position. Activation does not guarantee a fill. Available liquidity, the optional limit price, and market state still apply. Take-profit and stop-loss orders may be added, edited, or removed. A position may have at most 16. They are canceled when the position closes or changes direction. --- ## N1 Points N1 Points recognize participation in the N1 ecosystem. Points are distributed weekly during Season 1 and contribute to a user's Level and Momentum status. :::note Season 1 program status The Season 1 program is still being finalized. Eligibility rules, boost parameters, and dates may change before they are confirmed. ::: ## Points Each user has one private N1 Points balance. The owner can see their current and lifetime Points, weekly Points earned, and progress toward the next Level. There is no public Points leaderboard in Season 1. A user's public profile shows their Level and Momentum, but not their exact Points balance or progress toward the next Level. ## Total Points Season 1 is designed to distribute a fixed total of **1,987,000,000 N1 Points**. | Component | Points | | --- | ---: | | 26 live weeks at 75,000,000 Points per week | 1,950,000,000 | | Two retroactive weeks | 37,000,000 | | **Season 1 total** | **1,987,000,000** | Momentum and other boost parameters can change how a weekly pool is divided among eligible users. They do not increase the total number of Points in that pool or the Season 1 total. ## Levels Levels turn a user's cumulative Points into public progression. Levels are uncapped, and each successive Level is more difficult to reach. Levels are cosmetic. They make progression visible but do not increase a user's share of future Points. ## Momentum Momentum reflects consistent eligible activity over time. A user's public profile shows a Momentum state and may show the length of the active run. Momentum can increase the user's share of a weekly distribution, but it does not increase the weekly or Season 1 Points total. --- ## Account Value and PnL Account value combines the assets and perpetual positions held in one trading account. ## Trading PnL - **Unrealized PnL** is the gain or loss on an open position based on its entry price and the current mark price. - **Realized PnL** is the gain or loss recognized when a position is reduced or closed. ## Funding PnL Funding PnL is the funding payment accrued by an open position. It may be positive or negative and is shown separately from trading PnL. ## Available margin Available margin may differ from the displayed account value. Margin calculations account for: - the collateral value of deposited assets; - unrealized trading and funding PnL; - existing positions; and - the potential exposure of open orders. Collateral assets may contribute less than their full market value. See [Margining](../margining/) for the canonical definitions and risk calculations. All values are account-specific. Balances or PnL in another subaccount do not support the account. --- ## Order and Trade History The portfolio view separates current account state from historical activity. ## Current state Current state includes open orders, balances, positions, and active take-profit and stop-loss orders. ## Trade history Trade history may be filtered by market, side, and time range. Each fill records: - market; - side; - execution price and size; - fee; and - timestamp. RFQ executions are included in account activity alongside CLOB trades. ## Account history Account history includes position changes, PnL, funding, deposits, withdrawals, transfers, liquidations, fee-tier changes, and take-profit or stop-loss activity. PnL summaries aggregate realized and unrealized components over the selected period. --- ## Positions A perpetual position represents an account's open exposure in one market. | Field | Meaning | | --- | --- | | Side | Long or short | | Size | Quantity currently open | | Entry price | Average price at which the current position was opened | | Mark price | Current price used to calculate unrealized PnL | | Unrealized PnL | Current gain or loss on the open position | | Funding PnL | Funding accrued by the position | ## Position changes - A same-side fill increases the position and updates its average entry price. - An opposite-side fill realizes PnL on the quantity closed. - A larger opposite-side fill closes the old position and opens the excess in the new direction. - An equal opposite-side fill closes the position. When a position closes or changes direction, its take-profit, stop-loss, and incompatible reduce-only orders are canceled. --- ## Referrals The N1 referral program rewards traders who introduce new participants to the venue. ## Eligibility Set a username in the N1 app to enable your referral link. Referral benefits unlock after you reach \$1,000 in trading volume. ## Referral benefits Once benefits are unlocked, both the referrer and the referred trader receive benefits: **Referrer** - **10% fee share** from eligible trading fees generated by referred traders - **5% Points share** of the [N1 Points](./points) earned by referred traders **Referred trader** - **5% fee discount** on eligible trading fees The fee-share reward is calculated from trading fees, not trading volume. For example, if referred traders generate \$10,000 in eligible trading fees, the referrer earns \$1,000. The Points share is calculated from the N1 Points earned by referred traders. For example, if a referred trader earns 100 N1 Points, the referrer earns 5 N1 Points. The Referrals screen in the N1 app displays referral activity and paid and pending USDC rewards.