NightshadeDocs
Launch app

Order Book V4 Interface

Download the V4 contract interface package and integrate the on-chain order book with @alephium/web3.

The Order Book V4 contract interface package is everything a third‑party integrator needs to read Nightshade's on‑chain order book and place or cancel orders programmatically, without touching Ralph or reverse‑engineering the ABI by hand.

It's aimed at market makers, trading bots, analytics dashboards and any other application that wants to interact with Nightshade's order book contracts directly through @alephium/web3, rather than through the Nightshade web app.

What's included:

  • Contract ABI JSON for every V4 order book contract — functions, fields, events, maps, constants and enums — plus the compiled bytecode, which is already public on‑chain and is what the SDK needs to build a usable Contract instance.
  • Compiled TxScript templates for every script used to read or write the book.
  • The @alephium/web3 generated TypeScript bindings for all of the above, ready to import.

What's not included: the Ralph source code, test mocks, the V5/analytics/token contracts, or admin and upgrade scripts. Everything below is described in terms of behaviour, signatures and encodings, so you can integrate against the compiled artifacts alone.

The package is regenerated straight from the same compiled artifacts the Nightshade app itself uses — you're integrating against exactly what's live, not a hand‑maintained copy.

Download

FilePurpose
nightshade-orderbook-v4-interface.zipThe package
nightshade-orderbook-v4-interface.zip.sha256Checksum for the zip
MANIFEST.jsonThe same contents, unpacked — see also README.md

Verify the download before you unzip it:

curl -O https://docs.nightshade.finance/downloads/nightshade-orderbook-v4-interface.zip
curl -O https://docs.nightshade.finance/downloads/nightshade-orderbook-v4-interface.zip.sha256
shasum -a 256 -c nightshade-orderbook-v4-interface.zip.sha256

MANIFEST.json at the root of the package records the exact commit the artifacts were generated from, the @alephium/web3 version they were compiled against, the deployed contract addresses per network, and every contract's codeHash. Treat it as the source of truth if anything below ever looks stale.

Requirements & install

  • Node.js 18+
  • @alephium/web3 1.11.7 — pin this exact version. The bindings were generated by this SDK version and address contract methods by index; a different web3 release can change the generated API shape or the encoding it expects.
  • TypeScript with resolveJsonModule and esModuleInterop enabled (both commonjs and ESM module targets work).

Unzip the package into your project and import the bindings from the artifacts/ts folder:

unzip nightshade-orderbook-v4-interface.zip -d .
npm install @alephium/web3@1.11.7
import {
  OrderbookV4,
  OrderbookPairV4,
  OrderbookGetPriceVolumesPagedV4,
  OrderbookCreateBuyLimitOrderV4,
} from './nightshade-orderbook-v4-interface/artifacts/ts';
import { web3 } from '@alephium/web3';

// Public mainnet node. Testnet: https://wallet.testnet.alephium.org
web3.setCurrentNodeProvider('https://wallet.mainnet.alephium.org');

For signing transactions, use any SignerProvider — a browser wallet through @alephium/web3-react, or a headless PrivateKeyWallet from @alephium/web3-wallet.

Deployed contracts

These are the preserved deployments targeted by the downloadable interface package.

Every contract lives in group 0.

NetworkOrderbookV4 contract idAddress
Mainnet3b3b5973c29b3356178fe84dafc08faa8cb9493abca78ebd55ef34b9f47be000xgAezMDftDJaSD5KWTFA6bp8m9KyEnk9szy6yp2B5obu
Testnet0fbedef50bdf98b387a04fcce90f057ac3983d7ed50128ab43b98c5076e1ce00ukR31GVRg56BHYPBCMXSZJWJ73jhXFSGiyUzNUt4fdfM

OrderbookPairV4, PricePointsV4, PricePointOrdersV4 and FeeCollectorV4 are all created as sub‑contracts at runtime (see How the book is organised) — they don't have a single fixed address, but their code never changes, so you can verify their codeHash against the value in the package:

ContractcodeHash
OrderbookV433e0c0a29e3bc8a3d0cdf335dc48cc1919835e6a1a68c523a8d5c8c1e2641025
OrderbookPairV481e019a0acb3419e721d5e0a7a006e119e5ac7bc79ff383f240b755f7b93908b
PricePointsV4ef849a85abb5b9cdc7cb0ac9f9fc1e60fafb0da5aa61fb8b111fbaf589dd43fa
PricePointOrdersV447569d388131c26365065052a04c799afc72d577205cd19cc69fce71ac221197
FeeCollectorV4a8bbcca61e4bb3e4fb6db451f891ed85be53f004ea2c5b06037def4c14c74612

Verify a contract's codeHash against the node before trusting it:

curl "https://wallet.mainnet.alephium.org/contracts/xgAezMDftDJaSD5KWTFA6bp8m9KyEnk9szy6yp2B5obu/state"

Or from TypeScript, using the current node provider:

import { web3 } from '@alephium/web3';

const nodeProvider = web3.getCurrentNodeProvider();
const state = await nodeProvider.contracts.getContractsAddressState(
  'xgAezMDftDJaSD5KWTFA6bp8m9KyEnk9szy6yp2B5obu',
);
console.log(state.codeHash); // compare against MANIFEST.json / the table above

Mainnet's default fees today are defaultMakerFee = 200 and defaultTakerFee = 500, out of PERCENT_BASE_POINTS = 100_000 — 0.2% maker / 0.5% taker. Any individual pair can override these, so always read makerFee / takerFee off the pair's own state rather than assuming the default.

How the book is organised

Nightshade's V4 book is a small tree of contracts:

OrderbookV4 (root)
└── OrderbookPairV4  (one per market, e.g. TOKEN/ALPH)
    ├── PricePointsV4 (buy side)  → PricePointOrdersV4 per price level → orders
    ├── PricePointsV4 (sell side) → PricePointOrdersV4 per price level → orders
    └── FeeCollectorV4
  • The root OrderbookV4 tracks default fees and knows how to find or create a pair.
  • Each OrderbookPairV4 is a sub‑contract for one market — e.g. TOKEN/ALPH. It owns two sorted PricePointsV4 lists (one for buys, one for sells), a FeeCollectorV4, and a map orders: Map[U256, OrderV4] keyed by order id. lastOrderId on the pair's state is the last id it has issued.
  • Each PricePointsV4 is a sorted linked list of price levels for one side of the book.
  • Each price level is a PricePointOrdersV4 — a FIFO queue of order ids resting at that price. Matching is strict price‑time priority: best price first, then oldest order first within a price.

Finding a pair

A pair's sub‑contract path is firstTokenId ++ secondTokenId — the two 32‑byte token ids concatenated as hex. Order matters: (A, B) and (B, A) are different pairs. ALPH's token id is 64 zero hex characters.

You can derive a pair's address offline, with no RPC call:

import { addressFromContractId, subContractId } from '@alephium/web3';

function pairAddress(orderbookId: string, firstTokenId: string, secondTokenId: string): string {
  const path = firstTokenId + secondTokenId;
  const contractId = subContractId(orderbookId, path, 0);
  return addressFromContractId(contractId);
}

...or ask the root contract, which returns the pair's contract id (convert with addressFromContractId):

const orderbook = OrderbookV4.at(orderbookAddress);
const { returns: pairId } = await orderbook.view.getOrderbookPair({
  args: { firstTokenId, secondTokenId },
});

Neither of these checks that the market exists — the id is derived, not looked up. To find out, fetch the pair's state and treat a failure as "no market yet":

async function pairExists(pairAddress: string): Promise<boolean> {
  try {
    await OrderbookPairV4.at(pairAddress).fetchState();
    return true;
  } catch {
    return false;
  }
}

For example, on mainnet the pair for firstTokenId = 4e0515f9d7daabd7cdae603e355a9ea015b5288380ef2206a10a531bc858d600 and secondTokenId = ALPH (64 zeros) lives at 26b7yBbi3vfpZQ4nbr163nv7Fddcajbi4UDgzwRoUsJ71.

Order type, amounts and price

  • orderType: true = Buy, false = Sell.
  • buyAmount is always denominated in the second token's base units, sellAmount always in the first token's base units — on both sides of the book. A buy order pays buyAmount of the second token to receive sellAmount of the first token. A sell order pays sellAmount of the first token to receive buyAmount of the second token.
  • Price is second‑token base units per one first‑token base unit, as a fixed‑point number scaled by PRICE_PRECISION = 10^18. It's encoded, not stored raw, as priceDigits × 10^priceZeros, with priceDigits < MAX_PRICE_DIGITS (10,000) — i.e. four significant digits.
  • buyAmount and sellAmount must agree with the price: buyAmount = sellAmount × price / 10^18 exactly. Rather than compute this yourself, call the contract's own rounding‑safe views — exactBuyFromSellOrError (given a sellAmount and price, returns the exact [buyAmount, sellAmount] pair) or exactSellFromBuyOrError (the mirror, given a buyAmount) — before you submit an order. Both return a two‑element tuple, and the sellAmount they return can be slightly smaller than what you passed in.

Worked example. Both tokens use 18 decimals and you want to sell 2 whole tokens at a price of 1.25 (second token per first token):

  • sellAmount = 2 × 10^18 (2 tokens, first‑token base units)
  • price 1.25 × 10^18 encodes as priceDigits = 1250, priceZeros = 15 (1250 × 10^15 = 1.25 × 10^18)
  • buyAmount = sellAmount × price / 10^18 = 2×10^18 × 1.25×10^18 / 10^18 = 2.5 × 10^18

With those numbers a buy order pays 2.5 second tokens to receive 2 first tokens, and a sell order pays 2 first tokens to receive 2.5 second tokens. When the price is below 10^18, the sell amount is additionally rounded down to a multiple of 10,000 base units — another reason to let exactBuyFromSellOrError / exactSellFromBuyOrError compute the pair for you rather than hand‑rolling the arithmetic.

A buy fills resting sells at a price at or below its own price; a sell fills resting buys at a price at or above its own price.

Flip orders

Pass non‑zero flipPriceDigits/flipPriceZeros and, once your order fully fills, its proceeds are automatically re‑listed on the opposite side of the book at the flip price — this is how Nightshade's Automatic Liquidity works. For a normal, non‑flipping order pass 0, 0. Unless you're deliberately building flip orders, also pass flipHints: '' (an empty hex string).

Reading the book

Depth pages and best orders

OrderbookGetPriceVolumesPagedV4 returns up to 18 PriceVolumeOutputV4 { price, buyVolume, sellVolume, orderType } slots per call (unused slots have price = 0). Start with afterPrice = 0 for the best price, then pass the last non‑zero price back in to page further; a short page (fewer than 18 non‑zero slots) means you've reached the end of that side.

import { OrderbookGetPriceVolumesPagedV4 } from './nightshade-orderbook-v4-interface/artifacts/ts';
import type { PriceVolumeOutputV4 } from './nightshade-orderbook-v4-interface/artifacts/ts/types';

async function fetchDepth(orderbookId: string, firstTokenId: string, secondTokenId: string, orderType: boolean) {
  const levels: PriceVolumeOutputV4[] = [];
  let afterPrice = 0n;

  for (;;) {
    // `.call` is a dry run against the node: no signer, no gas.
    const { returns } = await OrderbookGetPriceVolumesPagedV4.call({
      initialFields: { orderbookId, firstTokenId, secondTokenId, orderType, afterPrice },
      groupIndex: 0,
    });
    const nonZero = returns.filter((slot) => slot.price !== 0n);
    levels.push(...nonZero);
    if (nonZero.length < 18) break;
    afterPrice = nonZero[nonZero.length - 1].price;
  }

  return levels;
}

OrderbookGetPriceVolumesV4 is the same call but only ever returns the first page — handy when you just want the top of the book.

OrderbookGetOrdersV4 (same leading fields, plus orderType) returns the best 18 resting OrderOutputV4 { fundingAddress, firstTokenId, secondTokenId, creator, price, buyAmount, sellAmount, orderType, flipPrice, swappedAmount, unlock } for that side — useful for showing individual orders rather than aggregated volume.

Quoting a market order before you send it

Two read‑only scripts let you preview a market order's outcome without spending gas on a failed or under‑filled swap:

  • GetBuyFromMarketBuy(orderbookId, firstTokenId, secondTokenId, buyerSellAmount, maxMatches) walks the sell side and answers "if I want buyerSellAmount of the first token, what does that cost?". It returns GetBuyFromMarketBuyResult { totalBuyAmount, totalSellAmount, remainingSellAmount, ordersMatched } — remainingSellAmount is what's left unfilled if the book runs dry before maxMatches.
  • GetSellFromMarketSell(orderbookId, firstTokenId, secondTokenId, sellerBuyAmount, maxMatches) walks the buy side and is the mirror.
  • GetBuyFromMarketSell and GetSellFromMarketBuy are the reverse‑direction variants — read their result struct to see which side they quote: GetBuyFromMarketSellResult carries remainingSellAmount (quoting how much of the first token a given sell amount buys), GetSellFromMarketBuyResult carries remainingBuyAmount (quoting how much of the second token a given buy amount sells).
import { GetBuyFromMarketBuy } from './nightshade-orderbook-v4-interface/artifacts/ts';

const { returns: quote } = await GetBuyFromMarketBuy.call({
  initialFields: {
    orderbookId,
    firstTokenId,
    secondTokenId,
    buyerSellAmount: 2n * 10n ** 18n,
    maxMatches: 25n,
  },
  groupIndex: 0,
});
// quote.totalBuyAmount, quote.totalSellAmount, quote.remainingSellAmount, quote.ordersMatched

Direct contract views

For a single known order or pair, call the contract directly rather than going through a script:

const pair = OrderbookPairV4.at(pairAddress);

const { returns: order } = await pair.view.getOrder({ args: { key: orderId } });
const { returns: exists } = await pair.view.containsOrder({ args: { key: orderId } });
const { returns: balances } = await pair.view.getBalances();
const state = await pair.fetchState();

OrderbookV4.at(orderbookAddress).view.getBalances({ args: { firstTokenId, secondTokenId } }) reads the same balances from the root without needing the pair's address first.

Subscribing to events

Every OrderbookPairV4 emits OrderCreated, OrderFilled, OrderUpdated, OrderCancelled and PricePointRemoved. Subscribe per event type:

const pair = OrderbookPairV4.at(pairAddress);

const subscription = pair.subscribeOrderCreatedEvent(
  {
    pollingInterval: 4000,
    messageCallback: async (event) => {
      console.log('order created', event.fields);
    },
    errorCallback: async (error) => {
      console.error(error);
    },
  },
  0, // fromCount
);

// later
subscription.unsubscribe();

subscribeAllEvents covers every event type on the contract in one subscription. If you'd rather poll than hold a subscription open, GET {NODE_URL}/events/contract/{pairAddress}?start=N returns raw events from count N onward.

Placing orders

Every script below takes orderbookId (hex contract id), firstTokenId and secondTokenId (hex) as its first three fields. Send a transaction with Script.execute(signer, { initialFields, attoAlphAmount, tokens }); dry‑run a read‑only script with Script.call({ initialFields, groupIndex: 0 }), which returns .returns and needs no signer.

Storage deposit and ALPH sizing

Creating an order deposits ALPH into contract storage; it's refunded when the order is cancelled or fully removed. Ask the contract for the exact figure rather than hard‑coding it:

const { returns: storageFee } = await OrderbookV4.at(orderbookAddress).view.getStorageFee({
  args: { isFlipPrice: false, maximumMatches: 25n },
});

As a rule of thumb, the Nightshade app itself sends:

  • Limit order: 0.4 ALPH (1.0 ALPH if it's a flip order) + 200 × DUST_AMOUNT, plus gas.
  • Market order: 0.4 ALPH + 51 × DUST_AMOUNT, plus gas.

If the token you're paying with is ALPH, add the paid amount on top of attoAlphAmount; otherwise pass the paid token in tokens: [{ id, amount }]. Any approved ALPH you don't end up needing is returned to you.

Limit order example (buy)

import { DUST_AMOUNT } from '@alephium/web3';
import { OrderbookV4, OrderbookCreateBuyLimitOrderV4 } from './nightshade-orderbook-v4-interface/artifacts/ts';

// `signer` is any SignerProvider (browser wallet or PrivateKeyWallet).
const orderbookId = '3b3b5973c29b3356178fe84dafc08faa8cb9493abca78ebd55ef34b9f47be000';
const orderbookAddress = 'xgAezMDftDJaSD5KWTFA6bp8m9KyEnk9szy6yp2B5obu';
const firstTokenId = '4e0515f9d7daabd7cdae603e355a9ea015b5288380ef2206a10a531bc858d600';
const secondTokenId = '0'.repeat(64); // ALPH

const orderbook = OrderbookV4.at(orderbookAddress);

// Buy 2 first tokens at price 1.25 ALPH per token (second-token base units per first-token base unit)
const wantedSellAmount = 2n * 10n ** 18n;
const priceDigits = 1250n;
const priceZeros = 15n;

// Let the contract compute the exact [buyAmount, sellAmount] pair for that price
const {
  returns: [buyAmount, sellAmount],
} = await orderbook.view.exactBuyFromSellOrError({
  args: { sellAmount: wantedSellAmount, price: priceDigits * 10n ** priceZeros },
});

const { returns: storageFee } = await orderbook.view.getStorageFee({
  args: { isFlipPrice: false, maximumMatches: 25n },
});

await OrderbookCreateBuyLimitOrderV4.execute(signer, {
  initialFields: {
    orderbookId,
    firstTokenId,
    secondTokenId,
    buyAmount, // ALPH you pay
    sellAmount, // tokens you receive
    priceDigits,
    priceZeros,
    maximumMatches: 25n,
    flipPriceDigits: 0n,
    flipPriceZeros: 0n,
    unlock: 0n,
    hint: 0n,
    flipHints: '',
    liquidityId: 0n,
  },
  // The paid token here is ALPH, so it rides along in attoAlphAmount; for any other
  // token pass `tokens: [{ id: secondTokenId, amount: buyAmount }]` instead.
  attoAlphAmount: storageFee + buyAmount + DUST_AMOUNT * 200n,
  tokens: [],
});

Use OrderbookCreateSellLimitOrderV4 with the same fields to place a sell instead.

Market order example

Market orders skip the price entirely: you state what you're paying and what quantity you want, and swapType decides what happens if the book can't fully satisfy that quantity.

  • swapType = 2 (immediate‑or‑cancel): fills as much of the wanted quantity as the book allows and refunds whatever payment goes unused.
  • swapType = 3 (fill‑or‑kill): reverts with MarketOrderCannotFill unless the whole wanted quantity is obtained.

For a buy market order, buyAmount is the amount of the second token you pay (and must approve/attach) and sellAmount is the quantity of the first token you want. For a sell market order, sellAmount is the amount of the first token you pay and buyAmount is the quantity of the second token you want. Neither field is a "minimum accepted" — sellAmount/buyAmount here is the target quantity, and swapType governs whether falling short is tolerated (IOC) or fatal (FOK).

import { DUST_AMOUNT } from '@alephium/web3';
import { OrderbookV4, OrderbookCreateBuyMarketOrderV4 } from './nightshade-orderbook-v4-interface/artifacts/ts';

const { returns: storageFee } = await OrderbookV4.at(orderbookAddress).view.getStorageFee({
  args: { isFlipPrice: false, maximumMatches: 25n },
});

await OrderbookCreateBuyMarketOrderV4.execute(signer, {
  initialFields: {
    orderbookId,
    firstTokenId,
    secondTokenId,
    buyAmount: 5n * 10n ** 18n, // paying up to 5 ALPH (second token here)
    sellAmount: wantedFirstTokenAmount, // quantity of the first token you want
    maximumMatches: 25n,
    swapType: 2n, // immediate-or-cancel: unused ALPH is refunded if the book can't fill it all
    flipHints: '',
  },
  attoAlphAmount: storageFee + 5n * 10n ** 18n + DUST_AMOUNT * 51n, // deposit + the ALPH being paid + dust
  tokens: [],
});

OrderbookCreateSellMarketOrderV4 mirrors this for the sell side: pass sellAmount as the first‑token amount you pay and buyAmount as the second‑token quantity you want.

Batching many orders

OrderbookCreateManyOrdersV4 submits up to 30 orders in one transaction, packed into a fixed‑size orderInputs: [OrderInputV4; 30] array of { buyAmount, priceBytes, flipPriceBytes, hint }, where priceBytes is a big‑endian uint32 priceDigits followed by a big‑endian uint32 priceZeros, hex‑encoded — and unused slots are zeroed‑out empty bytevecs (''). You also pass firstTokenAmount/secondTokenAmount (the totals across every order), buyOrderCount/sellOrderCount, and unlockAndLiquidityId — 16 bytes hex, a big‑endian uint64 unlock followed by a big‑endian uint64 liquidityId. Budget roughly (order count) × (1 ALPH + DUST_AMOUNT) plus whichever leg is paid in ALPH. It's a convenient way to seed a ladder of orders, but most integrators will reach for the single‑order scripts first.

Cancelling orders

OrderbookCancelOrderV4(orderbookId, firstTokenId, secondTokenId, orderId) cancels one order, refunds its tokens and storage deposit, and can only be called by the order's creator. It reverts with OrderLocked if called before the order's unlock timestamp has passed.

import { OrderbookCancelOrderV4 } from './nightshade-orderbook-v4-interface/artifacts/ts';

await OrderbookCancelOrderV4.execute(signer, {
  initialFields: { orderbookId, firstTokenId, secondTokenId, orderId },
});

OrderbookCancelManyOrdersV4(orderbookId, firstTokenId, secondTokenId, orderIds, numberOfOrders) cancels several at once — orderIds is a ByteVec of big‑endian uint32 order ids concatenated back to back.

Reference

TxScripts

ScriptPurposeKey fields
OrderbookCreateBuyLimitOrderV4Place a buy limit orderbuyAmount, sellAmount, priceDigits, priceZeros, maximumMatches, flipPriceDigits, flipPriceZeros, unlock, hint, flipHints, liquidityId
OrderbookCreateSellLimitOrderV4Place a sell limit ordersame shape as above
OrderbookCreateBuyMarketOrderV4Buy at market (IOC or FOK)buyAmount, sellAmount, maximumMatches, swapType, flipHints
OrderbookCreateSellMarketOrderV4Sell at market (IOC or FOK)same shape as above
OrderbookCreateManyOrdersV4Batch‑create up to 30 ordersfirstTokenAmount, secondTokenAmount, orderInputs[30], buyOrderCount, sellOrderCount, unlockAndLiquidityId
OrderbookCancelOrderV4Cancel one order (creator only)orderId
OrderbookCancelManyOrdersV4Cancel several orders at onceorderIds, numberOfOrders
OrderbookCreateOrderbookPairV4Create a market if it doesn't exist yet (anyone can call; costs 4× the minimal contract deposit + gas)—
OrderbookAddAutoLiquidityPairV4Public, but tied to Nightshade's own auto‑liquidity pool rotation — not intended for general integrator use—
OrderbookGetPriceVolumesPagedV4Read: paged depth for one sideorderType, afterPrice
OrderbookGetPriceVolumesV4Read: first page of depth for one sideorderType
OrderbookGetOrdersV4Read: best 18 resting orders on one sideorderType
GetBuyFromMarketBuyQuote: cost of buying a given amount at marketbuyerSellAmount, maxMatches
GetSellFromMarketSellQuote: proceeds of selling a given amount at marketsellerBuyAmount, maxMatches
GetBuyFromMarketSellQuote: mirror of the above, sell‑anchoredsellerSellAmount, maxMatches
GetSellFromMarketBuyQuote: mirror of the above, buy‑anchoredbuyerBuyAmount, maxMatches
OrderbookGetMaxBuyOrderV4Probe the best buy order (no return value; faults if the side is empty)orderType
OrderbookGetMinSellOrderV4Probe the best sell order (no return value; faults if the side is empty)orderType

Useful contract views

CallReturns
OrderbookV4.at(addr).view.getOrderbookPair({ firstTokenId, secondTokenId })The pair's contract id (derived, not an existence check)
OrderbookV4.at(addr).view.getStorageFee({ isFlipPrice, maximumMatches })Exact ALPH deposit for a new order
OrderbookV4.at(addr).view.getBalances({ firstTokenId, secondTokenId })A pair's token balances, looked up through the root
OrderbookPairV4.at(pair).view.getOrder({ key: orderId })Full OrderV4 struct for an order id
OrderbookPairV4.at(pair).view.containsOrder({ key: orderId })Whether an order id still exists
OrderbookPairV4.at(pair).view.getBalances()This pair's token balances
OrderbookV4.at(addr).view.exactBuyFromSellOrError({ sellAmount, price })Rounding‑safe [buyAmount, sellAmount] for a given sellAmount and price (also on the pair)
OrderbookV4.at(addr).view.exactSellFromBuyOrError({ buyAmount, price })Rounding‑safe [buyAmount, sellAmount] for a given buyAmount and price (also on the pair)

Events (per OrderbookPairV4)

EventFields
OrderCreatedids (ByteVec encoding orderId, liquidityId, originalOrderId), tokenPair, creator, buyAmount, sellAmount, orderType, flipPrice, unlock
OrderFilledorderId, firstTokenId, secondTokenId, creator, caller, orderType
OrderUpdatedorderId, firstTokenId, secondTokenId, creator, caller, buyAmount, sellAmount, orderType — a partial fill; the amounts are what remains
OrderCancelledorderId, firstTokenId, secondTokenId, caller, orderType
PricePointRemovedfirstTokenId, secondTokenId, pricePoint, orderType

Error codes

CodeName
0AdminAllowedOnly
1ExactPriceNotFound
2OrderCreatorOnly
3TooManyPriceDigits
4InvalidAmounts
5InvalidPaymentAmount
6OnlyOrderbookAllowed
7InvalidFlipPrice
8AutoLiquidityOrderCannotMatch
9AssetAddressesOnly
10OrderLocked
11UpgradeFrozen
12AutoLiquidityAlreadyExists
13MarketOrderCannotFill
14InvalidSwapType

Gotchas

Keep @alephium/web3 pinned to 1.11.7, the version the bindings were generated with. Never hand‑roll contract calls by method index — the indices are baked into the artifacts and the bindings already know them.

  • Everything described here lives in group 0.
  • Depth and order pages return 18 slots; always check for a short page rather than assuming a fixed page count.
  • Token ids are 32‑byte hex contract ids. Convert an address to one with binToHex(contractIdFromAddress(address)).
  • Don't try to deploy any of these contracts — the package gives you bytecode so the SDK can address the existing on‑chain contracts, not a template to redeploy.
  • Order creation and cancellation both move real ALPH for dust and storage deposits on top of whatever token you're trading — budget for it.
  • Matching is strict price‑time priority: a buy fills resting sells at or below its price, a sell fills resting buys at or above its price, oldest order first within a price level.

Support

Questions about the package or the on‑chain interface are welcome in Discord. The package is regenerated from the same compiled artifacts the Nightshade app itself uses, so if something here doesn't match on‑chain behaviour, treat it as a bug worth reporting.