Skip to content
markpaper

src/transport/weights.ts

v0.2.0 · 2.8 KB

Download file
// Request weights (documentation: docs.nado.xyz developer-resources/api/rate-limits).
//
// Documented limits: queries 2400 weight/min per IP (burst 400 / 10 s), executes 600/min per wallet
// (burst 100 / 10 s). Neither was measured against a live 429; the numbers feed the own throttle.
// Execute weights are the own-throttle convention (1 for place/cancel, 30 for link_signer), not documented.

import type { QueryRequest } from './types.js';

/** Documented query weights by `type`. */
export const QUERY_WEIGHTS: Readonly<Record<string, number>> = {
  contracts: 1,
  order: 1,
  market_price: 1,
  symbols: 2,
  subaccount_info: 2,
  nonces: 2,
  subaccount_orders: 2,
  linked_signer: 5,
  all_products: 5,
};

/** Weight per product id of `orders`. */
export const ORDERS_WEIGHT_PER_PRODUCT = 2;
/** Weight per product id of `market_prices` (approximate). */
export const MARKET_PRICES_WEIGHT_PER_PRODUCT = 1;
/** Weight assumed for query types that are not documented (`fee_rates`, ...). @experimental */
export const DEFAULT_QUERY_WEIGHT = 2;

/** Execute weights used by the own throttle. */
export const EXECUTE_WEIGHTS: Readonly<Record<string, number>> = {
  place_order: 1,
  cancel_orders: 1,
  cancel_product_orders: 1,
  link_signer: 30,
};
/** Weight of an execute action without an entry above. */
export const DEFAULT_EXECUTE_WEIGHT = 1;

function countIds(body: QueryRequest): number {
  const ids = body.product_ids;
  return Array.isArray(ids) ? ids.length : 0;
}

/** Weight of a query body: documented per type; `orders` and `market_prices` scale with `product_ids`. */
export function queryWeight(body: QueryRequest): number {
  if (body.type === 'orders') return Math.max(ORDERS_WEIGHT_PER_PRODUCT, ORDERS_WEIGHT_PER_PRODUCT * countIds(body));
  if (body.type === 'market_prices') return Math.max(1, MARKET_PRICES_WEIGHT_PER_PRODUCT * countIds(body));
  return QUERY_WEIGHTS[body.type] ?? DEFAULT_QUERY_WEIGHT;
}

/** The single action key of an execute body (`place_order`, `cancel_orders`, `link_signer`). */
export function executeAction(body: Record<string, unknown>): string | undefined {
  const keys = Object.keys(body);
  return keys.length === 1 ? keys[0] : undefined;
}

/** Weight of an execute body by its action. */
export function executeWeight(body: Record<string, unknown>): number {
  const action = executeAction(body);
  return (action && EXECUTE_WEIGHTS[action]) || DEFAULT_EXECUTE_WEIGHT;
}

/** Splits product ids into explicitly sized chunks. */
export function chunkProductIds(ids: readonly number[], size: number): number[][] {
  if (!Number.isInteger(size) || size <= 0)
    throw new RangeError(`chunk size must be a positive integer, got ${String(size)}`);
  const out: number[][] = [];
  for (let i = 0; i < ids.length; i += size) out.push(ids.slice(i, i + size));
  return out;
}
All files