Skip to main contentSkip to FAQSkip to contact

The @tetrafi/sdk package provides a typed, ergonomic interface for the TetraFi API. Stripe-inspired design: a single client instance with namespaced methods.

Bash
1npm install @tetrafi/sdk
2# or
3pnpm add @tetrafi/sdk
4# or
5yarn add @tetrafi/sdk
5 linesbash
TypeScript
1import { TetraFi } from "@tetrafi/sdk";
2
3const tetrafi = new TetraFi({
4 apiKey: process.env.TETRAFI_API_KEY!,
5 environment: "sandbox", // "sandbox" | "production"
6 timeout: 30000, // request timeout (ms), default 30000
7 retries: 3, // auto-retry on 5xx, default 3
8});
8 linestypescript
ParameterTypeDescription
apiKeyrequiredstringYour API key (sk_test_* for sandbox, sk_live_* for production)
environment"sandbox" | "production"API environment. Defaults to "production".
timeoutnumberRequest timeout in milliseconds. Defaults to 30000.
retriesnumberAuto-retry count on 5xx errors. Defaults to 3.
baseUrlstringOverride the API base URL. Useful for proxies or local development.
logger(msg: string) => voidCustom logging function. Defaults to console.log in development.

Submit a new Request for Quote to competing solvers.

ParameterTypeDescription
pairrequiredstringTrading pair in QUOTE/BASE format, e.g. "USDC/USDT"
siderequired"buy" | "sell"Trade direction from the taker's perspective
amountrequiredstringTrade size as a decimal string in source token units
corridorstringCross-chain path, e.g. "ethereum-optimism". Defaults to same-chain.
maxSlippagenumberMaximum acceptable slippage (0.001 = 0.1%). Defaults to 0.005.
TypeScript
1const rfq = await tetrafi.rfq.create({
2 pair: "USDC/USDT",
3 side: "buy",
4 amount: "1000000.00",
5 corridor: "ethereum-optimism",
6 maxSlippage: 0.001,
7});
7 linestypescript
ReturnsRFQ

Get competing quotes for an active RFQ.

TypeScript
1const quotes = await tetrafi.rfq.getQuotes("rfq_abc123");
1 linestypescript
ReturnsQuote[]- sorted best-price first

Automatically select and accept the best available quote.

TypeScript
1const settlement = await tetrafi.rfq.acceptBest("rfq_abc123");
1 linestypescript
ReturnsSettlement

Accept a specific quote and trigger escrow creation.

TypeScript
1const settlement = await tetrafi.quotes.accept("qt_xyz789");
1 linestypescript
ReturnsSettlement

Get the current status of a settlement.

TypeScript
1const settlement = await tetrafi.settlements.get("stl_abc123");
1 linestypescript
ReturnsSettlement

Check whether an address has a valid compliance attestation.

TypeScript
1const status = await tetrafi.compliance.check("...");
1 linestypescript

Fetch the WORM audit trail for a completed settlement.

TypeScript
1const entries = await tetrafi.audit.getTrail("stl_abc123");
1 linestypescript
ReturnsAuditEntry[]

Subscribe to real-time events via WebSocket.

TypeScript
1const unsubscribe = tetrafi.events.on("settlement.complete", (event) => {
2 console.log("Settled:", event.settlement.id);
3});
4
5// Later: stop receiving events
6unsubscribe();
6 linestypescript
ParameterTypeDescription
typerequiredEventTypeEvent type to subscribe to. Use wildcards: "settlement.*" matches all settlement events.
callbackrequired(event: Event) => voidFunction called when a matching event arrives.
TypeScript
1interface RFQ {
2 id: string;
3 pair: string;
4 side: "buy" | "sell";
5 amount: string;
6 corridor: string;
7 status: "pending" | "quoting" | "accepted" | "expired";
8 createdAt: string; // ISO 8601
9 expiresAt: string; // ISO 8601
10}
11
12interface Quote {
13 id: string;
14 rfqId: string;
15 solverId: string;
16 price: string; // Decimal string, e.g. "0.9995"
17 ttl: number; // Seconds until expiry
18 confidence: number; // 0-1 fill confidence score
19 createdAt: string;
20}
21
22interface Settlement {
23 id: string;
24 rfqId: string;
25 quoteId: string;
26 status: "pending" | "escrow_locked" | "filling" | "complete" | "failed" | "refunded";
27 originTxHash?: string;
28 destinationTxHash?: string;
29 amount: string;
30 createdAt: string;
31 settledAt?: string;
32}
33
34interface ComplianceStatus {
35 address: string;
36 isCompliant: boolean;
37 attestationHash: string;
38 expiresAt: string;
39}
40
41interface AuditEntry {
42 tradeId: string;
43 type: "rfq_created" | "quote_accepted" | "escrow_locked" | "settlement_complete";
44 timestamp: string;
45 data: Record<string, unknown>;
46 hash: string; // WORM integrity hash
47 prevHash: string; // Chain link to previous entry
48}
49
50type EventType =
51 | "rfq.created"
52 | "quote.received"
53 | "quote.accepted"
54 | "settlement.pending"
55 | "settlement.complete"
56 | "settlement.failed"
57 | "audit.recorded"
58 | "rfq.*"
59 | "settlement.*";
59 linestypescript
TypeScript
1import { TetraFi } from "@tetrafi/sdk";
2
3const tetrafi = new TetraFi({
4 apiKey: process.env.TETRAFI_API_KEY!,
5 environment: "sandbox",
6});
7
8async function executeSwap() {
9 // 1. Create RFQ
10 const rfq = await tetrafi.rfq.create({
11 pair: "USDC/USDT",
12 side: "buy",
13 amount: "1000000.00",
14 });
15 console.log("RFQ created:", rfq.id);
16
17 // 2. Wait for quotes (poll)
18 await new Promise((r) => setTimeout(r, 3000));
19 const quotes = await tetrafi.rfq.getQuotes(rfq.id);
20 console.log("Received quotes:", quotes.length);
21
22 if (quotes.length === 0) {
23 throw new Error("No quotes received");
24 }
25
26 // 3. Accept best price
27 const settlement = await tetrafi.rfq.acceptBest(rfq.id);
28 console.log("Settlement initiated:", settlement.id);
29
30 // 4. Monitor to completion
31 let status = settlement;
32 while (!["complete", "failed", "refunded"].includes(status.status)) {
33 await new Promise((r) => setTimeout(r, 5000));
34 status = await tetrafi.settlements.get(settlement.id);
35 console.log("Status:", status.status);
36 }
37
38 if (status.status === "complete") {
39 console.log("Success!");
40 console.log("Origin TX:", status.originTxHash);
41 console.log("Destination TX:", status.destinationTxHash);
42 } else {
43 throw new Error(`Settlement failed: {status.status}`);
44 }
45}
46
47executeSwap().catch(console.error);
47 linestypescript
TypeScript
1import { TetraFiError, RateLimitError, AuthError, ComplianceError } from "@tetrafi/sdk";
2
3try {
4 const rfq = await tetrafi.rfq.create({
5 pair: "USDC/USDT",
6 side: "buy",
7 amount: "1000000",
8 });
9} catch (e) {
10 if (e instanceof RateLimitError) {
11 // Exponential backoff
12 await sleep(e.retryAfter * 1000);
13 // retry...
14 } else if (e instanceof AuthError) {
15 console.error("Invalid API key - check TETRAFI_API_KEY");
16 } else if (e instanceof ComplianceError) {
17 console.error("Compliance check failed - ensure attestation is current");
18 } else if (e instanceof TetraFiError) {
19 console.error("API error:", e.code, e.message);
20 } else {
21 throw e; // Unknown error - rethrow
22 }
23}
23 linestypescript

For real-time updates, the SDK exposes a WebSocket event stream via tetrafi.events. The connection authenticates with your API key and supports wildcard channel subscriptions. Auto-reconnect is on by default.

TypeScript
1import { TetraFi } from "@tetrafi/sdk";
2
3const tetrafi = new TetraFi({
4 apiKey: process.env.TETRAFI_API_KEY!,
5 environment: "sandbox",
6});
7
8// Connect (lazy - happens on first .on() call)
9const unsubQuotes = tetrafi.events.on("quote.received", (event) => {
10 console.log(`[Quote] {event.quote.solverId}: {event.quote.price}`);
11});
12
13const unsubSettlement = tetrafi.events.on("settlement.*", (event) => {
14 switch (event.type) {
15 case "settlement.pending":
16 console.log("Escrow locked:", event.settlement.id);
17 break;
18 case "settlement.complete":
19 console.log("Complete!", event.settlement.originTxHash);
20 break;
21 case "settlement.failed":
22 console.error("Failed:", event.settlement.error);
23 break;
24 }
25});
26
27// Connection lifecycle hooks
28tetrafi.events.on("connection.opened", () => console.log("WS connected"));
29tetrafi.events.on("connection.closed", () => console.log("WS disconnected - auto-reconnecting"));
30tetrafi.events.on("connection.error", (e) => console.error("WS error:", e));
31
32// Cleanup on shutdown
33process.on("SIGTERM", () => {
34 unsubQuotes();
35 unsubSettlement();
36 tetrafi.events.disconnect();
37});
37 linestypescript

Auto-reconnect: the SDK reconnects automatically on connection loss with exponential backoff (1s → 2s → 4s → 8s, capped at 30s). Active subscriptions are restored after each reconnect - your .on() callbacks keep firing without re-subscribing.

ChannelWhen it fires
rfq.createdYour RFQ has been broadcast to solvers
quote.receivedA solver returned a quote
quote.acceptedYou accepted a quote
settlement.pendingSettlement initiated, escrow locking
settlement.completeBoth legs settled atomically
settlement.failedSettlement reverted, refund issued
audit.recordedNew WORM audit entry
rfq.*Wildcard - all RFQ events
settlement.*Wildcard - all settlement events

Event Stream

Subscribe to real-time quote and settlement events.

The @tetrafi/sdk package follows semver. Pin your dependency conservatively until you've confirmed compatibility in staging.

BumpMeaningAction required
1.0.x → 1.0.yPatch - bug fixes, no API changesAuto-update is safe
1.0.x → 1.1.xMinor - additive features, new methodsRead changelog, no migration
1.x.x → 2.0.0Major - breaking changesRead migration guide

Deprecation policy. Methods scheduled for removal are marked with a @deprecated JSDoc tag for at least one minor version before removal. Your IDE will surface a strikethrough on the call site, and the SDK logs a one-time warning at runtime:

text
1[TetraFi] Warning: tetrafi.rfq.create({ slippage }) is deprecated. Use { maxSlippage } instead. This will be removed in 2.0.0.
1 linestext

The full changelog lives at github.com/tetrafi/sdk-typescript/releases. Subscribe to the GitHub releases feed to be notified of new versions.

Pinning tip. For production deployments, pin to an exact version ("@tetrafi/sdk": "1.4.2") rather than a caret range ("^1.4.2"). This prevents an automatic minor update from changing behavior between deploys. Upgrade explicitly after reading the changelog.

Try the SDK directly in your browser:

How this works. Edit the code below and watch the output update live. The playground runs in a sandboxed iframe using a mock @tetrafi/sdk that returns realistic data with simulated latency - no real API calls are made. Try changing the amount, pair, or side to see how responses change.

Submit an RFQ

Create an RFQ, receive quotes, and accept the best price.