# SDK methods

> Subscribe to Solana BDN feed tracks with the raw MoQT client and deliver transactions over QUIC; the Preview typed Crypto surface.

The crypto modality does **not** ship a dedicated typed client yet. You use
the BDN through two surfaces that exist today:

- **Feeds** — subscribe to MoQT feed tracks with the raw MoQT client,
  `clutchcall_connect/moqt` (`MoqtClient`).
- **Transactions** — deliver a signed tx to the BDN over the **HTTP/3 submit
  API** (`/bdn/submit` and `/tpu`). Any HTTP/3 client can call it.

> **NOTE:**
> A typed `Crypto` class that wraps both surfaces is a **Preview** — see
> [Preview: the typed Crypto surface](#preview-the-typed-crypto-surface) at the
> bottom. Do not build against it as a shipping API.

## Subscribe to feeds with the MoQT client

A feed is an ordinary MoQT **frame track**. Thus you subscribe with the same
`MoqtClient` that the rest of the platform uses. Import it from the `moqt`
subpath.

  
```ts
import { MoqtClient, ConnectionState } from "clutchcall_connect/moqt";

const client = await MoqtClient.connect(
  "quic://feeds.firstbyte.fast",
  process.env.CLUTCHCALL_CREDENTIALS,               // bearer token
  (state, reason) => console.log("state", state, reason ?? ""),
);
```
  
  
```python
from clutchcall.moqt import MoqtClient

client = MoqtClient.connect(
    "quic://feeds.firstbyte.fast",
    auth_token="…",                               # bearer token
    on_state=lambda s: print("state", s),
)
```
  

`connect` returns immediately and dials in the background. The client
**reconnects automatically** with capped backoff. It **replays every
subscription** on reconnect. Thus a feed consumer survives a relay flap without
a new subscribe.

### `MoqtClient.connect`

- **`url`** (`string`, required) — The feed relay, e.g. `quic://feeds.firstbyte.fast`. The client discovers the   QUIC port from the host's DNS `HTTPS` record.
- **`token`** (`string`) — Bearer token for the feed scope. Private (`sol/feed/<orgId>/…`) tracks   require it. The public mainnet feed can be open.
- **`onState`** (`(state, reason?) => void`) — Lifecycle callback. `ConnectionState` is one of `Connecting`, `Connected`,   `Reconnecting`, `Closed`, `Failed`.
- **`options`** (`MoqtConnectOptions`) — `{ token?, serverCertificateHash?, webTransport? }`. Pass   `serverCertificateHash` when you pin a self-signed dev cert.

**Returns** `Promise<MoqtClient>`. The promise resolves when the first session
is up.

### `client.subscribeFrame(ns, name, onFrame)`

Subscribe to a feed track. The namespace and name together address the track
(`sol/feed/mainnet` + `slots`).

- **`ns`** (`string`, required) — Track namespace, e.g. `sol/feed/mainnet` (public) or `sol/feed/<orgId>`   (private).
- **`name`** (`string`, required) — Track name within the namespace: `slots`, `blockhash`, or `priorityFee`.
- **`onFrame`** (`(timestampUs: bigint, priority: number, data: Uint8Array) => void`, required) — The client calls this for every object that the relay fans out. `data` is the   raw frame payload. Decode it as the feed's JSON or binary schema.
- **`filterType`** (`number`) — Optional MoQT filter. The default is "largest object" (live tail). Pass a   start/end window to replay from a point in the durable origin.

**Returns** a `FrameSubscription`. Call `.close()` to unsubscribe.

  
```ts
const sub = client.subscribeFrame(
  "sol/feed/mainnet",
  "slots",
  (tsUs, _priority, data) => {
    const { slot } = JSON.parse(new TextDecoder().decode(data));
    console.log("slot", slot, "at", tsUs);
  },
);
// later: sub.close();
```
  
  
```python
def on_slot(ts_us: int, priority: int, data: bytes) -> None:
    import json
    print("slot", json.loads(data)["slot"], "at", ts_us)

sub = client.subscribe_frame("sol/feed/mainnet", "slots", on_slot)
# later: sub.close()
```
  

### Other `MoqtClient` members you'll use here

| Member | Purpose |
| ------ | ------- |
| `subscribeFrame(ns, name, onFrame)` / `subscribe_frame(...)` | Subscribe to a feed track (above). |
| `subscribeObject(ns, name, onObject)` / `subscribe_object(...)` | Subscribe to a **raw** object track (verbatim payload, no frame header). Use it for a custom binary feed. |
| `close()` | Close the session and every subscription. |

> **NOTE:**
> `publishFrame` also exists on `MoqtClient`, but **you do not publish feed
> tracks**. The BDN ingest publishes them. You set your ingest endpoint (public
> RPC or your own gossip/Geyser) on the server side. You only subscribe.

## Deliver a transaction (HTTP/3 submit API)

There is no typed submit method yet. Deliver the signed tx over HTTP/3 with the
raw bytes as the body. Both routes accept `application/octet-stream`.

- **`POST /bdn/submit`** (`octet-stream body`) — **Fan-out.** Query `leader=auto` (resolves the schedule) or   `leader=<host>:<port>`. The route ingests once, dedups on the 64-byte   signature, and fans across all edges. Each edge lands on its nearby leaders.   On success, it returns the signature.
- **`POST /tpu`** (`octet-stream body`) — **Direct.** Query `host=auto` or `host=<validator-host>&port=<quic-port>`.   The route sends the tx straight to the current + next leader TPU from this   node. There is no fan-out.

**cURL (HTTP/3):**
```bash
# BDN fan-out — lands on all edges, auto leader
curl --http3 \
  "https://crypto.firstbyte.fast/bdn/submit?leader=auto" \
  -H "Authorization: Bearer $CLUTCHCALL_CREDENTIALS" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @tx.bin
```
**TypeScript (fetch over HTTP/3):**
```ts
// Any HTTP/3-capable fetch. `signedTx` is the raw transaction bytes.
const res = await fetch("https://crypto.firstbyte.fast/bdn/submit?leader=auto", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLUTCHCALL_CREDENTIALS}`,
    "Content-Type": "application/octet-stream",
  },
  body: signedTx,           // Uint8Array, raw signed Solana tx
});
const { ok, sig } = await res.json();
```
**Python (httpx, HTTP/3):**
```python
import os, httpx
res = httpx.post(
    "https://crypto.firstbyte.fast/bdn/submit",
    params={"leader": "auto"},
    headers={
        "Authorization": f"Bearer {os.environ['CLUTCHCALL_CREDENTIALS']}",
        "Content-Type": "application/octet-stream",
    },
    content=signed_tx,        # bytes, raw signed Solana tx
)
print(res.json())             # {"ok": true, "sig": "…"}
```

### Submit query parameters

- **`leader`** (`string (/bdn/submit)`, default `auto`) — `auto` resolves the leader schedule. Pass `<host>:<port>` to pin a validator   TPU.
- **`host`** (`string (/tpu)`, default `auto`) — `auto` resolves the leader schedule. You can also pass an explicit validator   host. If you pass an explicit host, also pass `port=<quic-port>`.

The leg out to the validator uses Solana's native TPU QUIC protocol
(`ALPN solana-tpu`) with an **ephemeral ed25519** client identity per connection.

## Events & lifecycle

The feed surface is event-driven through the MoQT callbacks:

| Source | Event | When |
| ------ | ----- | ---- |
| `onState` | `Connecting` / `Connected` / `Reconnecting` / `Closed` / `Failed` | Session lifecycle. Tracks re-subscribe automatically on `Connected`. |
| `onFrame` | per feed object | Every slot / blockhash / fee update that the relay fans out. |
| `FrameSubscription.close()` | — | Unsubscribe a single feed track. |
| `MoqtClient.close()` | — | Close the session and every subscription. |

## Preview: the typed Crypto surface

> **WARNING:**
> **Preview — not shipping.** The shapes below are forward-looking. For real
> work, build against the MoQT client + HTTP/3 submit API above.

A typed `Crypto` client would wrap feed subscription and tx delivery behind one
handle. You would not write `subscribeFrame` calls or `fetch` bodies by hand:

```ts
// PREVIEW — illustrative shape only, not a shipping API.
import { Crypto } from "clutchcall_connect/crypto";

const crypto = new Crypto({
  key: process.env.CLUTCHCALL_CREDENTIALS,
  cluster: "mainnet",
});

// Feeds: an async iterator over a decoded feed track.
for await (const m of crypto.feeds.subscribe("slots")) {
  console.log("slot", m.slot);
}

// Submit: a typed result instead of a raw octet-stream POST.
const res = await crypto.submit(signedTx, { route: "bdn", leader: "auto" });
console.log("landed", res.sig, "in", res.ms, "ms");
```

Until that ships, the methods on this page are the supported surface.
