# Catalog & precision

Spot/Zipper catalogs, decimal strings, and scaled-integer inputs.

Every market has tick size, step size, and minimums. The SDK loads **spot** and **Zipper** catalogs so decimal inputs can be converted to wire integers safely.

## Wait for ready

Order and trigger **write** paths wait for background catalog hydration when `HydrateCatalogs: true` (default via `FromEnv`). You can still call this explicitly before reads that need symbol ids/scales:

```go
if err := client.WaitForCatalogs(ctx); err != nil {
    return err
}
```

Hydration is fail-closed: `WaitForCatalogs` returns an error when the attempt fails. See [Catalog](https://testnet.polyester.com/docs/sdk/go/reference/catalog).

## Decimal inputs

Use decimal strings (or money helpers) for human-facing qty/price. **Do not pass floats.** Strict decimal form is digits with an optional fractional part (`65000` / `65000.5`); bare trailing dots (`65000.`) are rejected. Leading/trailing whitespace is trimmed before validation.

```go
symbol := "BTC-USDT"
tif := "gtc"
price := models.PriceFromDecimal("64250.5")
_, err := client.Orders.Create(ctx, models.CreateOrderRequest{
    Symbol:   &symbol,
    Side:     "buy",
    OrderType: "limit",
    TIF:      &tif,
    Qty:      models.QtyFromDecimal("0.01"),
    Price:    &price,
    PostOnly: true,
}, nil)
```

> **post\_only is limit GTC only**
>
> Invalid `PostOnly` combinations return `*errors.ValidationError` before send.

Unscaled decimal quantities need a symbol (or an already-scaled quantity) so the SDK can resolve catalog scale. `BaseQuantityScaleForSymbol` returns `(scale, ok)` and never invents scale 8 for unknown or unhydrated symbols. Write paths return `*errors.ValidationError` instead of guessing. Scale-dependent reads and streams (orderbook, candles, public trades, and Zipper supply) also fail with a validation error when the required catalog scale is unavailable. Scale `0` is valid for whole-unit assets and is distinct from an unavailable scale (`ok == false`).

## Protocol scale ceiling

Quantity and ledger formatters, parsers, and catalog hydration reject scales above `codecs.MaxProtocolScale` (**36**). That ceiling is an SDK safety bound against pathological padding — it is **not** the ledger canonical scale. Ledger balances and `amount_e18` remain fixed at scale **18**. Trading quantity scales come from the spot catalog and are typically well below 36.

## Scaled integers (bots)

Use tick / scaled quantity constructors from `models` when your strategy already works in wire units. Protocol price ticks use fixed 1e6 scale; the server still validates market tick size.

Lookups:

```go
id := client.Catalogs.SymbolIDForSymbol("BTC-USDT")
scale, ok := client.Catalogs.BaseQuantityScaleForSymbol("BTC-USDT")
if id == nil || !ok {
    return fmt.Errorf("BTC-USDT is not present in the hydrated catalog")
}
```

Also see [Scaled integers](https://testnet.polyester.com/docs/developer-docs/connectrpc/scaled-integers) and [Public IDs](https://testnet.polyester.com/docs/developer-docs/shared-concepts/public-ids).
