Prices, sizes, and units
Every numeric field that represents a human price, size, balance, or volume is a decimal string — books, fills, candles, trades, and maker balances all share the same Decimal { value } wrapper. This is the load-bearing contract: get it right and everything downstream works.
Why strings
Floating-point arithmetic loses precision for large notionals. Decimal strings preserve the exact representation. Parse with whatever fixed-precision type your language offers:
| Language | Library |
|---|---|
| Rust | rust_decimal::Decimal |
| Python | decimal.Decimal |
| TypeScript | bignumber.js or decimal.js |
Conventions on the wire
| Field | Example | Meaning |
|---|---|---|
price | "155.14" | Quote per base, human-readable |
size | "10.0" | Base token units (e.g. SOL) |
volume | "42.137" | Candle base-asset volume |
open / high / low / close | "154.97" | Candle OHLC |
balance | "1.5" | Maker balance in token units (server scales by SpotMetadata.decimals) |
best_bid / best_ask / mid | strings | Optional, omitted on empty side |
You will never see on the wire:
- Lot multiples.
- Oracle units.
MakerService reports balances in human token units — the server applies SpotMetadata.decimals for you, so a 1.5-SOL balance arrives as Decimal("1.5"), not 1_500_000_000 atoms.
The SDK's quoting layer converts the human values you pass to the on-chain integer forms exactly once, at the point of building the instruction. The integer forms are documented in How Flint works for completeness — most integrators never have to think about them.
Pricing helpers
For when you need the conversion outside a QuoteBuilder call:
use sweetspot_api_client::pricing::{human_to_oracle, oracle_to_human, human_size_to_lots};
let market = config.pairs.iter().find(|p| p.base_name == "SOL").unwrap();
let oracle = human_to_oracle(155.14, market); // → 155_140
let back = oracle_to_human(oracle, market); // → 155.14
let lots = human_size_to_lots(10.0, market); // → 10_000Edge cases (saturate, NaN, negative) are documented in the Rust function-level docs.
Python exposes the same conversion helpers from flint.onchain, but human money inputs must be decimal.Decimal, decimal strings, or integers. Floats, NaN/Infinity, negatives, overflows, fractional atoms, and non-lot-aligned sizes raise instead of being rounded into quote bytes.
from decimal import Decimal
from flint.onchain import human_size_to_lots, human_to_oracle, oracle_to_human
oracle = human_to_oracle(Decimal("155.14"), market.units)
back = oracle_to_human(oracle, market.units)
lots = human_size_to_lots("10.0", market.units)Timestamps
Timestamp { micros }— Unix microseconds since epoch. Used on trades, candles, stream events, and range bounds.slot— Solana slot number. Useful for cross-referencing on-chain data.- Historical
startis inclusive. Historicalendis exclusive and defaults to server time when unset ormicros = 0.
Enums on the wire
Enum values use their proto variant names in examples and generated code:
| Field | Examples |
|---|---|
interval | "CANDLE_INTERVAL_5M", "CANDLE_INTERVAL_1H", "CANDLE_INTERVAL_1D" |
side | "SIDE_BUY", "SIDE_SELL" |
state | "HEALTH_STATE_HEALTHY", "HEALTH_STATE_DEGRADED" |
The SDKs all expose typed enums, so you don't have to hardcode the raw variant names or numbers.
