The crypto modality does not ship a dedicated typed client yet. You consume the BDN with two surfaces that exist today:
  • Feeds — subscribe to MoQT feed tracks with the raw MoQT client, @clutchcall/sdk/moqt (MoqtClient).
  • Transactions — deliver a signed tx to the BDN over the HTTP/3 submit API (/bdn/submit and /tpu), callable from any HTTP/3 client.
A typed Crypto class that wraps both surfaces is a Preview — see Preview: the typed Crypto surface at the bottom. Don’t build against it as a shipping API.

Subscribe to feeds with the MoQT client

A feed is an ordinary MoQT frame track, so you subscribe with the same MoqtClient the rest of the platform uses. Import it from the moqt subpath.
import { MoqtClient, ConnectionState } from "@clutchcall/sdk/moqt";

const client = await MoqtClient.connect(
  "quic://feeds.clutchcall.dev",
  process.env.CLUTCHCALL_CREDENTIALS,               // bearer token
  (state, reason) => console.log("state", state, reason ?? ""),
);
connect returns immediately and dials in the background. The client auto-reconnects with capped backoff and replays every subscription on reconnect, so a feed consumer survives a relay flap without re-subscribing.

MoqtClient.connect

url
string
required
The feed relay, e.g. quic://feeds.clutchcall.dev. The QUIC port is discovered from the host’s DNS HTTPS record.
token
string
Bearer token for the feed scope. Required for private (sol/feed/<orgId>/…) tracks; the public mainnet feed may 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 pinning a self-signed dev cert.
Returns Promise<MoqtClient> — resolves once 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
Called for every object 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 — defaults to “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.
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();

Other MoqtClient members you’ll use here

MemberPurpose
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) — for a custom binary feed.
close()Tear down the session and every subscription.
publishFrame exists on MoqtClient too, but you don’t publish feed tracks — the BDN ingest does. Your ingest endpoint (public RPC or your own gossip/Geyser) is configured server-side; you only ever 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 (resolve schedule) or leader=<host>:<port>. Ingests once, dedups on the 64-byte signature, fans across all edges; each edge lands on its nearby leaders. Returns the signature on success.
POST /tpu
octet-stream body
Direct. Query host=auto or host=<validator-host>&port=<quic-port>. Sends straight to the current + next leader TPU from this node, no fan-out.
# BDN fan-out — lands on all edges, auto leader
curl --http3 \
  "https://crypto.clutchcall.dev/bdn/submit?leader=auto" \
  -H "Authorization: Bearer $CLUTCHCALL_CREDENTIALS" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @tx.bin

Submit query parameters

leader
string (/bdn/submit)
default:"auto"
auto resolves the leader schedule; or <host>:<port> to pin a validator TPU.
host
string (/tpu)
default:"auto"
auto resolves the leader schedule; or an explicit validator host. Pair with port=<quic-port> when explicit.
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:
SourceEventWhen
onStateConnecting / Connected / Reconnecting / Closed / FailedSession lifecycle; tracks re-subscribe automatically on Connected.
onFrameper feed objectEvery slot / blockhash / fee update the relay fans out.
FrameSubscription.close()Unsubscribe a single feed track.
MoqtClient.close()Tear down everything.

Preview: the typed Crypto surface

Preview — not shipping. The shapes below are forward-looking. Build against the MoQT client + HTTP/3 submit API above for anything real.
A typed Crypto client would wrap feed subscription and tx delivery behind one handle, so you don’t hand-roll subscribeFrame calls or fetch bodies:
// PREVIEW — illustrative shape only, not a shipping API.
import { Crypto } from "@clutchcall/sdk/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.