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. Architecture

Architecture

The SDK is a thin, strongly typed layer over two wire protocols: ConnectRPC over HTTP for requests and Centrifugo over WebSocket for realtime. Everything else exists to make those two safe and ergonomic.

The layers

PolyesterEnvironment          frozen endpoint + chain configuration
        │
PolyesterClient               wires everything, exposes services as lazy getters
  ├── Transports              two ConnectRPC transports: publicApi and authApi
  ├── RealtimeClient          one shared WebSocket multiplexer
  ├── ClientCatalog           reference data: assets, pairs, scales, routes
  └── Services                orders, marketData, balances, … (26 domains)
  • Environment — a validated, frozen description of where to connect (see Environments).
  • Transports — publicApi carries unauthenticated traffic; authApi attaches auth (JWT header or Ed25519 request signature) via an interceptor. An error-mapping interceptor wraps both, so every RPC failure surfaces as a typed PolyesterError.
  • Services — one class per API domain, exposed as getters on the client. Services are constructed on first access and memoized, so creating a client is nearly free — important when a server builds one per request.
  • RealtimeClient — a single shared subscription multiplexer. Channels are refcounted; the WebSocket engine itself (~300 KB) is dynamically imported on the first subscription.
  • Catalog — the reference-data store every service consults for symbol lookups and decimal scale conversions (see Catalog & precision).

Validation at both edges

Every service method validates in both directions with strict schemas:

  • Inputs are checked before the request is built — unknown keys, wrong enum values, and excess decimal precision throw a ValidationError / CatalogConversionError locally, with no network round-trip.
  • Outputs are parsed into plain, documented TypeScript shapes. Wire-level representations (scaled bigints, proto enums, oneofs) never leak through: you get decimal strings and string literal unions.

This means the compiler and the runtime agree: if it typechecks and doesn't throw, what you sent is exactly what the API received.

The decimal-string surface

The wire protocol carries scaled integers (price ticks, per-asset scaled quantities). The SDK's public surface carries plain decimal strings in both directions:

  • Outputs convert exactly — no rounding, trailing zeros trimmed.
  • Inputs convert strictly — excess precision is an error, never rounded away.

Floating-point number is never used for money.

Why three clients

PolyesterClient is the whole machine. The two subclasses only add an authentication strategy and a subaccount resolver — the hook that lets service calls default to the user's active subaccount:

Client Auth strategy Resolver default
PolyesterClient Whatever auth you pass (API key / JWT) none (main account)
PolyesterBrowserClient Managed wallet-signer login + token storage the active account from auth state
PolyesterServerClient Bearer token parsed from cookies main, or display-session active account (opt-in)

Bundle discipline

The root export stays lean deliberately. Heavy dependency graphs are isolated behind subpath exports so an app shell that only reads market data never bundles them:

Subpath Isolated weight
@polyester/sdk/account-signer viem ABI / typed-data graph for Safe signature derivation
@polyester/sdk/smart-account permissionless + bundler/paymaster clients
@polyester/sdk/catalogs snapshot builders (types are free from the root)
@polyester/sdk/server-session cookie/session parsing for servers

The realtime engine follows the same principle at runtime via dynamic import.

Extension points

  • interceptors — ConnectRPC interceptors run on every request (logging, tracing, mock headers).
  • wireFormat — "binary" (default) or "json" for human-readable debugging.
  • realtime — override WebSocket auth header derivation.
  • catalog / catalogSnapshot / catalogCell — take control of reference-data storage, hydration, and reactivity.
Previous

Overview

Next

Authentication

  • The layers
  • Validation at both edges
  • The decimal-string surface
  • Why three clients
  • Bundle discipline
  • Extension points