# Minimal product: NEAR Intents demand monitor ## Goal Build the smallest useful event-driven product for crypto trading research: - read **live user demand** from NEAR Intents - publish demand into a **central Kafka/Redpanda-compatible bus** - prove downstream consumption with a **dummy reactor** - avoid dashboards, execution, wallets, storage, auth workflows beyond the required API key, strategy code, and generic infra beyond the message bus itself ## Why this is the right first slice From the NEAR Intents docs, there are several possible data surfaces: 1. **Message Bus WebSocket `quote` subscription** - Endpoint: `wss://solver-relay-v2.chaindefuser.com/ws` - Real-time stream for quote requests - Subscription request shape: ```json { "jsonrpc": "2.0", "id": 1, "method": "subscribe", "params": ["quote"] } ``` - Expected live frame shape is JSON-RPC-like but should be treated as flexible. The adapter should accept quote payloads when the useful fields appear either: - directly under `params` - directly under `result` - or at the top level of the message body - Fields of interest include: - `quote_id` (or equivalent request identifier) - `defuse_asset_identifier_in` - `defuse_asset_identifier_out` - `exact_amount_in` or `exact_amount_out` - `min_deadline_ms` - Subscription acknowledgements may also vary. They may arrive as an `id`-matched JSON-RPC response with a simple `result`, a structured `result`, or other non-quote control frame before the first quote event. - This is the closest public signal to **current demand**. 2. **Message Bus JSON-RPC `publish_intent` / `get_status`** - Endpoint: `https://solver-relay-v2.chaindefuser.com/rpc` - Useful for posting intents or checking a known `intent_hash` - Not a public firehose of all intents. 3. **Explorer API `/api/v0/transactions`** - Historical and analytics friendly - Requires JWT auth - Better for history, not best for a minimal live monitor 4. **Verifier contract intent payloads** - The on-chain swap expression is usually `token_diff` - Important for understanding settlement semantics - Not the easiest first live intake path for a lean bus-first system ## Product decision The minimal product should monitor **WebSocket `quote` events** and route them through a bus-first runtime. ### Why - closest live signal to user demand - directly reflects what users are requesting from solvers - enough to answer the first trading question: **what assets are being requested right now?** - decouples venue intake from downstream analysis through Kafka-compatible topics ### Important implementation note Current docs for the market-maker quickstart and live endpoint behavior indicate the Message Bus requires a **partner API key / JWT** in the `Authorization: Bearer ...` header. That means the best path is still the quote stream, but live operation is partner-gated. ### Important caveat A `quote` event is **pre-trade demand**, not guaranteed execution. That is fine for v0. The purpose is demand sensing, not settlement accounting. ## Runtime shape ```text NEAR Intents websocket | v src/apps/near-intents-ingest.mjs | +--> raw.near_intents.quote | +--> norm.swap_demand | v src/apps/dummy-consumer.mjs ``` ### Runtime contracts #### Ingest app `src/apps/near-intents-ingest.mjs`: - loads env - parses optional `--pair 'asset_a->asset_b'` - starts the NEAR Intents websocket adapter - writes raw and normalized events to the configured broker #### Dummy consumer `src/apps/dummy-consumer.mjs`: - subscribes to `norm.swap_demand` - logs observed pair and quote id - exists only to prove a downstream consumer contract #### Bus config Default env-driven topics and group ids: - `KAFKA_TOPIC_RAW_NEAR_INTENTS_QUOTE=raw.near_intents.quote` - `KAFKA_TOPIC_NORM_SWAP_DEMAND=norm.swap_demand` - `KAFKA_CONSUMER_GROUP_DUMMY=dummy-reactor-v1` Redpanda is a valid runtime target because the transport is Kafka-compatible. ## Internal model Normalize each quote event into a thin bus envelope: Top-level envelope fields: - `venue` - `source` - `type` - `eventId` - `occurredAt` - `ingestedAt` - `assetIn` - `assetOut` - `raw` - `quote` Nested `quote` fields: - `quoteId` - `assetIn` - `assetOut` - `amountIn` - `amountOut` - `ttlMs` Field extraction must remain tolerant to known upstream aliases, and normalization should continue to operate on the merged `metadata + data` payload shape from the Message Bus event. The live adapter now intentionally accepts quote-like payloads from `params`, `result`, or the top-level message body, but only processes frames that actually look like quote data. Subscription acknowledgements and unrelated control frames should still be ignored. ## Filtering The ingest runtime supports an optional exact-pair filter: ```bash npm run near-intents:ingest -- --pair 'asset_a->asset_b' ``` The filter is direction-agnostic, so the reversed asset order is also accepted. ## Scope boundaries ### Must do - connect to the websocket - subscribe to `quote` and tolerate control frames - normalize quote events into one compact model - publish raw and normalized events to Kafka/Redpanda-compatible topics - allow a downstream consumer to react to normalized events - reconnect automatically on disconnect - document `npm` and `node` entrypoints ### Must not do - Python packaging or CLI guidance - TUI-specific product requirements - charts - account details - pnl - routing internals - market making controls - execution buttons - config panels - speculative infra beyond the current bus and dummy consumer ## Path to success 1. Connect to WebSocket 2. Subscribe to `quote` 3. Normalize incoming events into one compact model 4. Publish raw envelopes to `raw.near_intents.quote` 5. Publish normalized envelopes to `norm.swap_demand` 6. Start a dummy consumer on the normalized topic 7. Reconnect automatically on disconnect 8. Only after this works, consider: - `quote_status`-specific downstream handling - historical replay via Explorer API - token metadata enrichment - filtering and alerts beyond `--pair` ## Packaging alignment Current repository packaging and usage should stay aligned around the JavaScript runtime entrypoints: - package scripts: - `npm run near-intents:ingest` - `npm run dummy-consumer` - `npm start` as a compatibility wrapper - direct app entrypoints: - `node src/apps/near-intents-ingest.mjs` - `node src/apps/dummy-consumer.mjs` Documentation should treat the npm scripts and `src/apps/*` node entrypoints as canonical. Older single-file and Python/TUI instructions should remain removed to avoid runtime confusion. ## Sources - NEAR Intents Message Bus WebSocket docs: `subscribe` with `quote` / `quote_status` - NEAR Intents Message Bus RPC docs: `quote`, `publish_intent`, `get_status` - Verifier contract docs: `token_diff` intent type - Explorer API OpenAPI: authenticated historical transactions