Concept · Telemetry

A flat event interface, any sink

SdkTelemetry is a single-method interface. The SDK calls event() at named points; you decide where the data goes. Plausible, PostHog, Datadog, your own /telemetry endpoint — all behind the same shape.

The interface#

SdkTelemetry ships at @tokenops/sdk/telemetry:

  • event(name, props) — sync, fire-and-forget
  • scope(tags) — returns a child telemetry that auto-tags every event

Names follow <product>.<surface>.<action> (e.g. fhe-vesting.claim.submitted). Props are flat — string / number / boolean — so they survive any sink without special-casing.

Built-in adapters#

NoopTelemetry — the default. Zero work, zero cost. Production code that never wires telemetry pays nothing.

ConsoleTelemetry — dev-time logger. Prefixes every event with the SDK product so noise is easy to filter.

TokenOpsTelemetry — POSTs to the hosted ingestion at telemetry.tokenops.xyz. Useful if you want bounty-cohort friction to land in our analytics for feedback prioritisation.

Plug your own#

lib/telemetry.ts
ts
import { withTelemetry, type SdkTelemetry } from "@tokenops/sdk/telemetry";
import { createConfidentialVestingManagerClient } from "@tokenops/sdk/fhe-vesting";

// Implement the interface against your sink — Datadog, OpenTelemetry,
// Sentry, PostHog, your own /telemetry endpoint, whatever.
const adapter: SdkTelemetry = {
  event(name, props) {
    fetch("/internal/telemetry", { method: "POST", body: JSON.stringify({ name, props }) });
  },
  scope(tags) {
    // Bind tags into a child telemetry
    return { ...adapter, event: (n, p) => adapter.event(n, { ...tags, ...p }) };
  },
};

const manager = withTelemetry(
  createConfidentialVestingManagerClient({ publicClient, walletClient, address }),
  adapter,
);

withTelemetry(client, adapter) wraps any SDK client. The wrapper is the same shape, so you can drop it in without touching call-sites.

What gets emitted#

The SDK emits events at the natural breakpoints: tx submitted, tx mined, encryptor invoked, preflight result, error caught. The full list is in the API reference. Props NEVER include addresses, hashes, or handles — the SDK's scrubProps belt drops suspicious keys at the adapter boundary.

See also