The interface#
SdkTelemetry ships at @tokenops/sdk/telemetry:
event(name, props)— sync, fire-and-forgetscope(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#
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.