TESTNET
Markets
Trade
Lending Vaults
More
User Docs Developer Docs Sdk API Docs Help
Overview
Overview
Architecture
Authentication
Client configuration
Overview
Environments
Installation
Market data
Market data services
Authentication model
Quickstart
Trading
Trading services
Account services
Catalog & precision
Streaming
Accounts & balances
Funding services
Deposits & withdrawals
Realtime client
Errors
Server-side usage
Error handling
  1. Typescript
  2. /
  3. Catalog & precision

Catalog & precision

Two facts shape the SDK's data model:

  1. The wire protocol carries scaled integers — a price is 64250500000n ticks, a quantity is an integer in per-asset units. Exact, compact, and unambiguous — but only meaningful if you know each asset's scale.
  2. JavaScript's number cannot represent money safely.

The SDK resolves both with one design: a catalog of reference data (which knows every scale), and a public surface of exact decimal strings (which never touches floating point).

The decimal-string contract

  • Every price/quantity output is a decimal string, converted exactly from the wire integer — no rounding, trailing zeros trimmed. 1500000n at scale 6 becomes "1.5".
  • Every input is converted strictly. "0.1234567" for a 6-decimal field throws a CatalogConversionError — the SDK never rounds your order silently.
  • Prices are always quoted at 6 decimal places (PRICE_SCALE = 6); quantity scales vary per asset and come from the catalog.

Use a decimal library (or plain string math) for arithmetic; the strings parse cleanly into anything.

What the catalog holds

client.catalog is a snapshot of venue reference data with typed readers on top:

Reader Contents
catalog.market Trading assets and pairs: symbols ⇄ ids, listing status, precision, price/amount conversions
catalog.ledger Ledger assets: id/symbol lookups, amount conversions and display formatting
catalog.orders Per-pair order constraints (tick, step, minimums) + order input validation
catalog.zipper Deposit/withdraw chains, unified assets, per-chain routes, contracts
await client.catalog.ensureReady();

const pair = client.catalog.market.requirePairBySymbol("BTC-USDC");
const constraints = client.catalog.orders.getSpotOrderConstraints("BTC-USDC");
const route = client.catalog.zipper.requireAssetChain("USDC", "ethereum");

// conversions & display helpers
client.catalog.market.normalizePriceInput("64250.512345678", "BTC-USDC"); // truncate to precision
client.catalog.ledger.formatAmount("1234.5", "USDC"); // display-rounded

get* variants return null on a miss; require* variants throw a CatalogLookupError.

Lifecycle: ready, refresh, states

The catalog starts empty and fills itself from two public endpoints (spot config + zipper config) the first time something needs it:

  • ensureReady() — resolve the current snapshot, fetching if empty. Service methods await this internally; call it yourself before direct catalog reads.
  • ready() — passive: resolves the current snapshot or in-flight refresh, never starts one.
  • refresh() — force a refetch (deduped while in flight).
  • state() — empty, refreshing, fresh, or stale (kept serving the old snapshot after a failed refresh).

Realtime subscriptions gate event delivery behind readiness: publications that arrive before scales are known are buffered and flushed in order.

Unknown assets never crash
Events referencing assets the catalog doesn't know resolve to a sentinel “unknown asset” (isUnknownLedgerAsset / isUnknownZipperAsset) rather than throwing mid-stream, and orders on unknown symbols are filtered out of list responses.

Owning the snapshot

By default each client fetches and holds its own snapshot. Three config options change that:

  • catalogSnapshot — seed the client with a snapshot you already have (typically from SSR: the server calls catalog.snapshot(), bakes it into the page, the browser client starts warm).
  • catalogCell — back the snapshot with external storage you control. A reactive cell (a store, a signal) makes every catalog read reactive — one snapshot shared across clients and UI.
  • catalog — inject a fully managed ClientCatalog instance (advanced; mutually exclusive with the other two).

The @polyester/sdk/catalogs subpath exports the builders (createPolyesterCatalog, buildCatalogSnapshot, createCatalogSnapshotReader) and all reader/config types for these integration patterns.

Previous

Account services

Next

Streaming

  • The decimal-string contract
  • What the catalog holds
  • Lifecycle: ready, refresh, states
  • Owning the snapshot