Skip to content
All documentation chapters
On this page

Physical trade observations (FA and legacy)

GET /api/v1/fa/trades and GET /api/v1/legacy/trades expose read-only rows from trade_events and trade_events_legacy respectively. They are not /swaps summary rows, nor rows from the derived physical_swap_events table. IDs are local to each table. BUY/SELL projections, both flows and summary records must not be summed as independent deduplicated executions.

Request

Parameter Contract
network Optional, mainnet by default; existing network validator
token Required exact metadata: 0x + 1–64 hex digits, optionally ::MoveModule::MoveType; case-sensitive, no symbol matching, no implicit padding
from_block, to_block Required inclusive nonnegative block bounds; to_block - from_block <= 200000
limit Default 50, 1–500 using the existing bounded parameter validator
before_block, before_id Optional paired cursor from meta.next_cursor; ID positive, block inside the original interval
offset Only omitted or 0; use the cursor for subsequent pages

Keep network, token and both bounds unchanged while paging. Ordering is block_height DESC, id DESC. A limit+1 read determines has_more and next_cursor without inventing a total. Cursor paging avoids offset shifts; it is not a database snapshot and late inserts above an already consumed cursor require a new traversal. Empty results cover only the specified identity and block interval.

The token matches tracked_token_metadata, counter_token_metadata, token_in_metadata or token_out_metadata with binary case-sensitive comparison. Preserve the registered Move case and persisted FA address spelling.

Response

The standard ok/data/meta envelope applies. Rows contain id, network, tx_hash, block_height, event_index, dex, source, side; tracked/counter metadata, symbols and amounts; input/output metadata, symbols and amounts; created_at. Added flow is fa/legacy and record_kind is physical_trade_observation. Amounts are nullable decimal strings; zero and negative observations are preserved. Internal raw payloads, metadata, identity hashes and source-row pointers are not exposed.

created_at is recording time in the API's UTC database session, NOT block time. This table contract does not expose a block timestamp, canonical completeness or selected physical lineage. Presence is not proof of candle eligibility.

meta includes endpoint, network, token, flow, record_kind, from_block, to_block, sort=block_desc_id_desc, limit, count, has_more and next_cursor (before_block/before_id or null). Invalid identity, missing/reversed/excessive bounds, invalid cursor or nonzero offset return BAD_REQUEST.

Access and bounded reads

Routes use the same entry-point API-key validation (including database key/client/plan state and IP restrictions), configured rate limiting, request logging and sanitized error handling as /swaps. No new scopes or commercial entitlements are invented: the current entry point does not enforce per-route scopes/history entitlements; configured-key scopes alone are not an authorization check. This existing limitation remains a commercial policy decision.

Reads force idx_trade_events_block (migration 002) or idx_trade_events_legacy_block (migration 011) over a required bounded block range, with network and exact-token residual predicates. Missing indexes fail through the existing INTERNAL_ERROR path: no full-table fallback. These block indexes are not network/token composite indexes; bounded range density and possible sorting must be measured on stage. A block-span limit does not guarantee a fixed number of rows scanned or fixed latency. No migration or index changes are required.

Relation to canonical candles

MarketCandleCanonicalSource uses physical_swap_events as its fallback, not these trade observation rows directly. A derived physical row needs complete_raw/complete_fallback, positive resolved block time, distinct input/output identities and positive input/output amounts. The tracked side must be active. A selected terminal summary for the same network/transaction/directed pair suppresses the fallback. The runtime materializer must also allow the exact directed pair. Trades against WHY do not become SUPRA-quoted candles through an implicit conversion.

Publishing these endpoints diagnoses the observation stage; the trade routes do not trigger materialization or historical recovery.

Observed active pairs in runtime materialization

With market.runtime_materialization.observed_active_pairs=true, the running indexers materialize supported directional pairs touched by new canonical events when the base token is active in the registry. Explicit configured pairs remain supported. FA and legacy evidence retain the existing canonical precedence and deduplication; reciprocal candles are generated only when the canonical source exposes that direction for an active base token. No Cartesian product of tokens, synthetic swaps or historical replay is implied.

/market/pair and /candles still require an exact network/token/quote/scope. Non-SUPRA quotes remain denominated in that quote; this does not introduce indirect USD or WHY-to-SUPRA conversions. Empty results are not evidence of an absence of physical trades. Activation covers future processed events, not historical events already passed by the cursors.

Stats refresh uses batches of at most 100 pairs inside the existing atomic block flush. The FA expiry coordinator visits configured pairs and existing all/all stats in bounded, rotating batches; more than 100 pairs are not truncated. Expiry uses the existing network event clock, not wall time. A traversal can require multiple 300-block polling checkpoints, so it does not promise a 300-second wall-clock update deadline. Legacy event pairs are materialized by the legacy process and their persisted stats are included in the FA expiry traversal.