ID Model¶
Both feedd and orderd operate on a single shared identity space. Every message on the wire references assets and instruments by a u64 identifier derived from a deterministic canonical key.
Canonical key grammar¶
Canonical keys have two forms:
<category>.<namespace>:<locator> # general form
<category>.<namespace> # no-locator form (only where explicitly supported)
The no-locator form is used for assets where the namespace itself is sufficient to identify the entity (for example native.solana, native.btc).
| Segment | Role | Assets | Instruments |
|---|---|---|---|
| category | Entity kind | Asset kind (native, erc20, spl, syn) |
Instrument type (spot, perp) |
| namespace | Hosting environment | Chain family (evm, solana, btc) or venue (binance) |
Venue (binance, coinbase, hyperliquid) |
| locator | Authoritative identifier | Chain-/venue-specific identifier (see patterns below) | Venue-native trading symbol |
Both entity types share the same parser, the same hash function, and the same u64 output width.
Asset locator patterns¶
| Pattern | Meaning | Example |
|---|---|---|
| (none) | Namespace uniquely identifies the asset | native.solana, native.btc |
<chain_id> |
Native asset on a specific chain in a family | native.evm:1 |
<chain_id>_<address> |
Contract asset on a specific chain | erc20.evm:1_0xa0b86991... |
<symbol> |
Venue custodial balance symbol | syn.binance:eth |
Asset key examples¶
| Canonical key | Description |
|---|---|
native.evm:1 |
ETH on mainnet |
erc20.evm:1_0xa0b86991... |
USDC on mainnet |
native.evm:8453 |
ETH on Base |
native.solana |
SOL |
spl.solana:EPjFWdd5... |
USDC on Solana |
native.btc |
BTC |
syn.binance:eth |
ETH balance on Binance |
syn.coinbase:btc |
BTC on Coinbase |
syn.hyperliquid:spot-btc |
BTC spot balance on Hyperliquid |
Instrument key examples¶
| Canonical key | Description |
|---|---|
perp.binance:BTCUSDT |
BTC-USDT perpetual on Binance |
spot.binance:BTCUSDT |
BTC-USDT spot on Binance |
perp.bybit:BTCUSDT |
BTC-USDT perpetual on Bybit |
spot.coinbase:BTC-USD |
BTC-USD spot on Coinbase |
perp.hyperliquid:BTC |
BTC perpetual on Hyperliquid |
perp.deribit:BTC-PERPETUAL |
BTC perpetual on Deribit |
Normalization rules¶
| Segment | Rule |
|---|---|
category |
MUST be lowercase |
namespace |
MUST be lowercase |
locator (assets, EVM) |
MUST be lowercase hex, 0x-prefixed, 40 characters |
locator (assets, Solana) |
MUST preserve exact base58 bytes — Solana mints are case-sensitive |
locator (assets, venue balances) |
SHOULD be lowercase for case-insensitive venues; MUST preserve exact bytes for case-sensitive venue symbols |
locator (instruments) |
MUST preserve venue casing exactly — the exchange is the authority |
ID derivation¶
The hash function is deterministic: the same canonical key always produces the same u64. At 500K entities the collision probability is approximately 10⁻¹¹.
Design principles¶
- Asset locators are chain-authoritative. The EVM contract address or Solana mint is a fact published by the chain, not a value assigned by this system.
- Instrument locators are venue-authoritative. The venue-native symbol (e.g.
BTCUSDT) is preserved exactly as the exchange publishes it. No normalization is applied. - Decomposition is metadata, not identity. An instrument's base, quote, and settlement asset breakdown is carried in metadata fields. The canonical key itself is opaque — consumers resolve it via the metadata snapshot.
- Same asset, different locations produce different
asset_idvalues. ETH on mainnet, ETH on Base, and ETH on Binance are three distinct assets. Risk-tier metadata links related assets throughunwrap_toandrisk_root_id.
Common pitfalls
- Do not assume two assets with the same ticker are the same
asset_id.syn.binance:ethandnative.evm:1are distinct entities. - Do not infer instrument decomposition from the venue symbol.
BTCUSDTon Binance andBTC-USDon Coinbase have different quote assets. Always consult the metadata. - Solana mint addresses are case-sensitive. Use the canonical key as-is; do not lowercase.
Core metadata¶
The minimum required for both binaries to function and for consumers to interpret messages correctly.
Asset¶
Packed fixed-size struct (16 bytes):
offset size type field
─────────────────────────────
0 8 u64 asset_id
8 1 u8 venue 0 for on-chain assets
9 1 u8 decimals
10 1 u8 status ACTIVE | HALTED | DELISTED | PENDING
11 1 u8 _pad
12 4 u32 meta_seq
─────────────────────────────
16 bytes total
Instrument¶
Packed fixed-size struct (56 bytes):
offset size type field
─────────────────────────────
0 8 u64 inst_id
8 8 u64 base_id base / underlier asset_id
16 8 u64 quote_id quote / denomination asset_id
24 8 u64 settle_id settlement asset_id (= quote_id for spot)
32 4 u32 price_tick_mantissa
36 4 u32 qty_step_mantissa
40 2 i16 make_fee_bps maker fee in basis points (negative = rebate)
42 2 i16 take_fee_bps taker fee in basis points
44 1 i8 price_tick_exponent
45 1 i8 qty_step_exponent
46 1 u8 venue
47 1 u8 inst_type SPOT | PERP
48 1 u8 status ACTIVE | HALTED | DELISTED | PENDING
49 3 _pad
52 4 u32 meta_seq
─────────────────────────────
56 bytes total
The make_fee_bps and take_fee_bps fields are best-effort estimates, periodically refreshed by the metadata pipeline. Fee accuracy is not guaranteed — see Fees.
Tick and step encoding¶
Tick sizes and quantity steps are encoded as mantissa × 10^exponent, avoiding floating-point precision issues entirely.
| Instrument | Mantissa | Exponent | Resolved value |
|---|---|---|---|
| BTC-USDT perpetual (Binance) | 1 | -1 | 0.1 |
| ETH-USDT spot (Binance) | 1 | -2 | 0.01 |
| SHIB-USDT (Binance) | 1 | -8 | 0.00000001 |
| BTC-USD (Coinbase) | 1 | 0 | 1 |
Status enum¶
Replaces a simple boolean gate with a richer lifecycle state.
| State | Value | Meaning |
|---|---|---|
ACTIVE |
1 | Normal operation |
HALTED |
2 | Temporarily suspended (exchange maintenance, circuit breaker) |
DELISTED |
3 | Permanently removed — consumers SHOULD remove from local state |
PENDING |
4 | Known to the system but not yet ready for use |
Consumers MUST NOT submit orders or interpret prices for any entity whose status is not ACTIVE.
Fees¶
Fees on crypto exchanges are a function of both the instrument and the account. Maker/taker rates for the same instrument vary across accounts depending on volume tier, VIP status, and venue-specific rebate programs. Venues may change an account's tier at any time.
What the metadata pipeline provides¶
The metadata pipeline periodically queries upstream venues and writes the most recent fee values into the make_fee_bps and take_fee_bps fields on the instrument struct. These values are best-effort — they reflect the last successful poll and may be stale.
Fee accuracy CANNOT be guaranteed. There is inherent latency between a tier change upstream and its propagation through the metadata pipeline, and some venues do not expose per-account fee schedules programmatically at all.
What consumers SHOULD do¶
For fee-sensitive strategies, consumers MUST treat fill messages as the authoritative fee source:
- Read the actual maker/taker fee reported on each fill message returned by
orderd. - Maintain a local fee cache keyed by
inst_id, updated from observed fills. - Use the metadata fee fields as an initial estimate until fills arrive.
The metadata fees are adequate for rough pre-trade cost estimates and order routing heuristics. They are NOT suitable for P&L calculations, reconciliation, or fee accounting.
Fills are the source of truth for fees
The instrument struct fee fields are best-effort. For any strategy where fee accuracy matters, read fees from fill messages and cache them locally.
Invariants¶
Violations indicate a data integrity issue. Consumers SHOULD NOT use the affected entity until the violation is resolved.
status == ACTIVEis required before using an asset or instrument for trading or pricinginst_type ∈ {SPOT, PERP}→base_id,quote_id, andsettle_idMUST all be non-zeroinst_type == SPOT→settle_idMUST equalquote_id
String table, venue registry, risk metadata, and delivery mechanics are documented in Metadata.