npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@titanexchange/sdk-ts

v0.1.2

Published

Client SDK for the Titan API

Readme

Titan SDK for TypeScript

Official TypeScript/JavaScript SDK for the Titan Swap API - Stream live swap quotes from multiple DEXes and liquidity providers on Solana.


Overview

The Titan SDK provides a WebSocket-based client for requesting and receiving live streams of token swap quotes on Solana. The API aggregates quotes across multiple decentralized exchanges and liquidity providers, delivering continuously updated simulated results at configurable intervals. The SDK handles connection management, quote streaming, and returns ready-to-execute transactions.

Key Features:

  • Live quote streaming with continuous updates from multiple providers
  • Automatic compression negotiation (zstd, brotli, gzip)
  • Pre-built transactions ready for signing and execution
  • TypeScript support with full type definitions
  • Browser and Node.js compatible

Table of Contents


Installation

# npm
npm install @titanexchange/sdk-ts

# yarn
yarn add @titanexchange/sdk-ts

# pnpm
pnpm add @titanexchange/sdk-ts

Requirements: Node.js >=18.19


Basic Usage

To get started with the Titan SDK, you'll connect to the API, stream live quotes, and execute swaps. This section walks through the core workflow.

Connecting to the API

Establish a WebSocket connection to the Titan API using your authentication token.

import { V1Client } from "@titanexchange/sdk-ts";

// Connect with authentication token in query string
const url = "wss://YOUR_API_ENDPOINT/ws?auth=YOUR_AUTH_TOKEN";
const client = await V1Client.connect(url);

// Check connection status
console.log("Connected:", !client.closed);

// Monitor connection close events (optional)
client.listen_closed().then((event) => {
  console.log("Connection closed:", event.code, event.reason);
});

// Close connection when done
await client.close();

Notes:

  • Connection automatically negotiates compression (zstd, brotli, gzip, or none)
  • Authentication token is passed as a query parameter
  • listen_closed() returns a promise that resolves when the connection closes
  • Contact [email protected] for API-related inquiries

Streaming Swap Quotes

Once connected, you can request a continuous stream of swap quotes. The API will send you updated quotes at regular intervals from multiple providers, allowing you to compare prices in real-time.

import { V1Client, types } from "@titanexchange/sdk-ts";
import bs58 from "bs58";

// Example token mint addresses (replace with actual mints)
const USDC = bs58.decode("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // USDC mainnet
const SOL = bs58.decode("So11111111111111111111111111111111111111112");  // Wrapped SOL
const USER_PUBKEY = bs58.decode("YOUR_WALLET_PUBLIC_KEY_HERE"); // Replace with your wallet

const { stream, streamId, response } = await client.newSwapQuoteStream({
  swap: {
    inputMint: USDC,              // Pubkey (32-byte Uint8Array)
    outputMint: SOL,              // Pubkey (32-byte Uint8Array)
    amount: 1_000_000,            // Uint64 - raw token amount (not scaled)
    swapMode: types.common.SwapMode.ExactIn,
    slippageBps: 50,              // Optional - defaults to server setting
    dexes: ["Raydium", "Whirlpool"],   // Optional - filter to specific DEXes
    excludeDexes: ["Phoenix"],    // Optional - exclude DEXes
    // providers: ["provider_id"], // Optional - filter by provider ID
  },
  transaction: {
    userPublicKey: USER_PUBKEY,   // Required for transaction generation
    feeBps: 10,                   // Optional - fee in basis points
  },
  update: {
    num_quotes: 3,                // Number of top quotes per update
    intervalMs: 1000,             // Optional - update interval (server has min/max)
  },
});

// Consume quote stream using async iteration
for await (const quotes of stream) {
  for (const [providerId, route] of Object.entries(quotes.quotes)) {
    console.log(`${providerId}:`, {
      inAmount: route.inAmount,
      outAmount: route.outAmount,
      slippageBps: route.slippageBps,
      instructions: route.instructions.length,
    });
  }
}

Swap Modes:

  • SwapMode.ExactIn - Amount is input tokens, slippage applied to output
  • SwapMode.ExactOut - Amount is output tokens, slippage applied to input

Note: Swap mode selection is not currently supported.

Optional Filters:

  • dexes / excludeDexes - Control which DEX venues are used
  • providers - Limit to specific provider IDs
  • onlyDirectRoutes - Only allow direct swaps (no intermediate tokens)

Important Notes:

  • num_quotes uses snake_case (not numQuotes)
  • The quotes field is an object with provider names as keys, not an array
  • Transform to array: Object.entries(quotes.quotes).map(([provider, route]) => ({ ...route, provider }))

Executing Swaps

When you've found a quote you want to execute, the SDK provides a pre-built transaction ready for signing and submission to Solana.

import { Connection, VersionedTransaction } from "@solana/web3.js";

// Get the best quote
const bestRoute = Object.values(quotes.quotes)[0];

// Deserialize and sign the transaction
const tx = VersionedTransaction.deserialize(bestRoute.transaction);
tx.sign([yourKeypair]); // or use wallet.signTransaction(tx) in browser

// Send to Solana
const connection = new Connection("https://api.mainnet-beta.solana.com");
const signature = await connection.sendTransaction(tx);
await connection.confirmTransaction(signature);

Note: Quotes have expiration times (expiresAtMs, expiresAfterSlot). Execute promptly for best results.

Stopping a Stream

If you want to stop receiving quotes before the stream ends naturally, you can cancel it manually. This is useful when you've already executed a swap or no longer need updates.

const { stream, streamId } = await client.newSwapQuoteStream(params);

// Method 1: Using client.stopStream() with stream ID
let count = 0;
for await (const quotes of stream) {
  console.log("Quote:", quotes.id);

  if (++count >= 5) {
    const result = await client.stopStream(streamId);
    console.log("Stopped stream:", result.id);
    break;
  }
}

// Method 2: Using stream.cancel() directly
const { stream } = await client.newSwapQuoteStream(params);

setTimeout(() => {
  stream.cancel("Timeout reached"); // Reason is optional
}, 10000);

for await (const quotes of stream) {
  console.log("Quote:", quotes.id);
}

Notes:

  • Both methods send a stop request to the server
  • stopStream(streamId) returns a confirmation with the stream ID
  • cancel(reason?) internally calls stopStream() on the server

Understanding Instruction Format

For advanced use cases, you might need to work with raw Solana instructions. The SDK uses compact field names to minimize bandwidth usage.

interface Instruction {
  p: Uint8Array;      // programId
  a: AccountMeta[];   // accounts
  d: Uint8Array;      // instruction data
}

interface AccountMeta {
  p: Uint8Array;      // pubkey
  s: boolean;         // isSigner
  w: boolean;         // isWritable
}

Getting Server Info

Before making requests, you can query the server to learn about supported settings, parameter limits, and protocol version. This helps you configure requests within valid ranges.

const info = await client.getInfo();

console.log("Protocol version:", {
  major: info.protocolVersion.major,
  minor: info.protocolVersion.minor,
  patch: info.protocolVersion.patch,
});

console.log("Quote update settings:", {
  intervalMs: info.settings.quoteUpdate.intervalMs,    // { min, max, default }
  num_quotes: info.settings.quoteUpdate.num_quotes,    // { min, max, default }
});

console.log("Swap settings:", {
  slippageBps: info.settings.swap.slippageBps,         // { min, max, default }
  onlyDirectRoutes: info.settings.swap.onlyDirectRoutes,
  addSizeConstraint: info.settings.swap.addSizeConstraint,
});

console.log("Connection limits:", {
  concurrentStreams: info.settings.connection.concurrentStreams, // number
});

Notes:

  • Use this to check parameter bounds before creating swap quote requests
  • concurrentStreams tells you how many streams you can run simultaneously
  • Quote update and slippage settings include min, max, and default values

Listing Venues and Providers

You can query which DEX venues and liquidity providers are available on the platform. This is helpful for filtering requests to specific sources.

// Get list of DEX venues
const venues = await client.getVenues();
console.log("Venues:", venues.labels); // ["Raydium""Whirlpool""Phoenix", ...]

// Get venues with Solana program IDs
const venuesWithIds = await client.getVenues({ includeProgramIds: true });

// Get list of quote providers
const providers = await client.listProviders();
providers.forEach((p) => {
  console.log(`${p.name} (${p.kind}): ${p.id}`);
});

// Get providers with icon URLs
const withIcons = await client.listProviders({ includeIcons: true });

Notes:

  • Use venue labels in dexes/excludeDexes filters
  • Use provider IDs in providers filter
  • Provider kind indicates the provider type

Getting Swap Prices

If you need a quick price check without opening a continuous quote stream, use getSwapPrice(). This method returns a single price quote based on the best simulated route, making it ideal for displaying estimated prices or performing one-time price checks.

import { V1Client } from "@titanexchange/sdk-ts";
import bs58 from "bs58";

const USDC = bs58.decode("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // Example: USDC token address
const SOL = bs58.decode("So11111111111111111111111111111111111111112");  // Example: Wrapped SOL token address

const price = await client.getSwapPrice({
  inputMint: USDC,
  outputMint: SOL,
  amount: 1_000_000,           // Raw token amount (not scaled by decimals)
  dexes: ["Raydium", "Whirlpool"],  // Optional - filter to specific DEXes
  excludeDexes: ["Phoenix"],   // Optional - exclude specific DEXes
});

console.log("Price ID:", price.id);
console.log("Input amount:", price.amountIn);
console.log("Output amount:", price.amountOut);
console.log("Input mint:", price.inputMint);
console.log("Output mint:", price.outputMint);

When to use getSwapPrice() vs newSwapQuoteStream():

  • Use getSwapPrice() for one-time price checks, UI previews, or displaying estimated rates
  • Use newSwapQuoteStream() when you need continuous updates, multiple quotes from different providers, or ready-to-execute transactions

Optional Filters:

  • dexes - Limit price calculation to specific DEX venues
  • excludeDexes - Exclude specific DEX venues from price calculation

Notes:

  • This method does not generate executable transactions
  • Returns a single price based on the best simulated route
  • Response includes a unique id for tracking the price quote

Browser Usage

The SDK works in modern browsers with the same API as Node.js. Import from the /browser entrypoint:

import { V1Client } from "@titanexchange/sdk-ts/browser";

Protecting Your API Key

When using the SDK in a browser (client-side), your API key must remain protected. Any credentials in frontend code can be extracted through browser devtools or by inspecting network traffic.

Solution: Implement a middleware proxy

Set up a backend middleware service that:

  • Accepts WebSocket connections from your frontend
  • Authenticates requests using your own session/auth mechanism
  • Proxies connections to the Titan API with the API key injected server-side

Middleware Proxy Example

Below is an example middleware solution that securely handles Titan API authentication on the server side. You can use this as a starting point or implement your own middleware solution based on your application's requirements and tech stack.

Note: The main objective is to protect your Titan API key from being exposed in client-side code. Feel free to implement your own middleware based on your tech stack and requirements.

See examples/middleware.ts for the complete working example.

Server code:

import { createServer } from "http";
import { env } from "node:process";
import pkg from "websocket";

// Destructure server and client from websocket package
// (CommonJS modules require this pattern for ES module imports)
const { server: WebSocketServer, client: WebSocketClient } = pkg;

// Load configuration from environment variables
const PORT = parseInt(env["PORT"] || "3000");
const TITAN_WS_URL = env["TITAN_WS_URL"];
const TITAN_API_KEY = env["TITAN_API_KEY"];

// Titan API sub-protocols (for compression negotiation)
const TITAN_SUBPROTOCOLS = [
  "v1.api.titan.ag+zstd",
  "v1.api.titan.ag+brotli",
  "v1.api.titan.ag+gzip",
  "v1.api.titan.ag",
];

// Create HTTP server and WebSocket server
const httpServer = createServer();
const wsServer = new WebSocketServer({ httpServer });

/**
 * Validate user token before allowing proxy access.
 * Replace with your actual authentication logic.
 */
function validateUserToken(token: string | null): boolean {
  if (!token) return false;
  // TODO: Implement your validation (JWT verify, session lookup, etc.)
  return token.length > 0;
}

// Handle WebSocket requests
wsServer.on("request", (request) => {
  // Extract user token and validate
  const url = new URL(request.resource, `http://localhost:${PORT}`);
  const token = url.searchParams.get("token");

  if (!validateUserToken(token)) {
    request.reject(401, "Unauthorized");
    return;
  }

  // Select sub-protocol for compression
  const requestedProtocols = request.requestedProtocols;
  let selectedProtocol: string | null = null;
  for (const proto of requestedProtocols) {
    if (TITAN_SUBPROTOCOLS.includes(proto)) {
      selectedProtocol = proto;
      break;
    }
  }

  // Accept client connection
  const clientConnection = request.accept(selectedProtocol, request.origin);

  // Connect to Titan API with API key (server-side only)
  const titanClient = new WebSocketClient();
  let titanConnection: ReturnType<typeof request.accept> | null = null;

  titanClient.on("connect", (connection) => {
    titanConnection = connection;

    // Forward Titan responses to browser client
    connection.on("message", (message) => {
      if (clientConnection.connected && message.binaryData) {
        clientConnection.sendBytes(message.binaryData);
      }
    });

    connection.on("close", (code, desc) => {
      if (clientConnection.connected) clientConnection.close(code, desc);
    });
  });

  // Connect to Titan with API key and sub-protocol
  titanClient.connect(
    `${TITAN_WS_URL}?auth=${TITAN_API_KEY}`,
    selectedProtocol ? [selectedProtocol] : undefined
  );

  // Forward browser client messages to Titan API
  clientConnection.on("message", (message) => {
    if (titanConnection && message.binaryData) {
      titanConnection.sendBytes(message.binaryData);
    }
  });

  // Clean up on disconnect
  clientConnection.on("close", () => {
    if (titanConnection?.connected) titanConnection.close();
  });
});

// Start the proxy server
httpServer.listen(PORT, () => {
  console.log(`Proxy running at ws://localhost:${PORT}/titan-proxy`);
});

Configuration (environment variables):

| Variable | Description | |----------|-------------| | TITAN_WS_URL | Titan API WebSocket URL (as provided) | | TITAN_API_KEY | Your Titan API key | | PORT | Server port (default: 3000) |

Key concepts:

  1. Accept WebSocket connections from browser clients
  2. Validate user authentication (implement your own logic)
  3. Handle Titan API sub-protocols for compression negotiation
  4. Connect to Titan API with your API key (server-side only)
  5. Proxy messages bidirectionally between client and Titan API

Production considerations:

  • Use TLS (wss://) in production
  • Implement rate limiting
  • Add request logging and monitoring

Browser Client Example

Your browser code connects to your backend proxy, not directly to the Titan API:

import { V1Client } from "@titanexchange/sdk-ts/browser";

// Connect to your backend proxy with user token
const client = await V1Client.connect("ws://localhost:3000/titan-proxy?token=USER_TOKEN");
const { stream } = await client.newSwapQuoteStream(params);

for await (const quotes of stream) {
  console.log("Quotes:", quotes);
}

Notes:

  • Browser builds include WebSocket and compression polyfills
  • API is identical to Node.js usage
  • Contact [email protected] for API-related inquiries

Types

The SDK exports TypeScript types for all request and response structures. Import them from the types namespace for better type safety and autocomplete.

import { types } from "@titanexchange/sdk-ts";

// Access types via namespace
types.v1.SwapQuoteRequest
types.v1.SwapParams
types.common.SwapMode

Request Types

interface SwapQuoteRequest {
  swap: SwapParams;
  transaction: TransactionParams;
  update?: QuoteUpdateParams;
}

interface SwapParams {
  inputMint: Uint8Array;          // 32-byte Pubkey
  outputMint: Uint8Array;         // 32-byte Pubkey
  amount: number | bigint;        // Uint64 - raw amount
  swapMode?: SwapMode;           
  slippageBps?: number;
  dexes?: string[];
  excludeDexes?: string[];
  providers?: string[];
  onlyDirectRoutes?: boolean;
  addSizeConstraint?: boolean;
  sizeConstraint?: number;
}

interface TransactionParams {
  userPublicKey: Uint8Array;          // 32-byte Pubkey
  feeBps?: number;
  feeAccount?: Uint8Array;            // 32-byte Pubkey
  feeFromInputMint?: boolean;
  outputAccount?: Uint8Array;         // 32-byte Pubkey
  closeInputTokenAccount?: boolean;
  createOutputTokenAccount?: boolean;
}

interface SwapPriceRequest {
  inputMint: Uint8Array;        // 32-byte Pubkey
  outputMint: Uint8Array;       // 32-byte Pubkey
  amount: number | bigint;      // Uint64 - raw token amount
  dexes?: string[];             // Optional - filter to specific DEXes
  excludeDexes?: string[];      // Optional - exclude specific DEXes
}

Response Types

interface SwapQuotes {
  id: string;
  inputMint: Uint8Array;
  outputMint: Uint8Array;
  swapMode: SwapMode;
  amount: number;
  quotes: { [providerId: string]: SwapRoute };
}

interface SwapRoute {
  inAmount: number;
  outAmount: number;
  slippageBps: number;
  platformFee?: PlatformFee;
  steps: RoutePlanStep[];
  instructions: Instruction[];
  addressLookupTables: Uint8Array[];  // Pubkey[]
  contextSlot?: number;
  timeTaken?: number;
  expiresAtMs?: number;
  expiresAfterSlot?: number;
  computeUnits?: number;
  computeUnitsSafe?: number;
  transaction?: Uint8Array;
  referenceId?: string;
}

interface SwapPrice {
  id: string;                   // Identifier for this price quote
  inputMint: Uint8Array;        // 32-byte Pubkey
  outputMint: Uint8Array;       // 32-byte Pubkey
  amountIn: number | bigint;    // Uint64 - input amount used for pricing
  amountOut: number | bigint;   // Uint64 - output amount from best simulated route
}

Handling BigInt values

The API may return BigInt for large numbers (Uint64 > 53 bits). For logging:

function serializeForDisplay(data: any): string {
  return JSON.stringify(data, (key, value) => {
    if (typeof value === "bigint") {
      return value.toString() + "n";
    }
    if (value instanceof Uint8Array) {
      return `<Uint8Array: ${value.length} bytes>`;
    }
    return value;
  }, 2);
}

Error Handling

The SDK throws specific error types for different failure scenarios. Catch and handle them to build robust applications.

import { V1Client, client } from "@titanexchange/sdk-ts";


const {
  ConnectionClosed,
  ConnectionError,
  ErrorResponse,
  StreamError,
  ProtocolError
} = client;

try {
  const c = await V1Client.connect(url);
  const { stream } = await c.newSwapQuoteStream(params);

  for await (const quotes of stream) {
    console.log("Quotes:", quotes);
  }
} catch (err) {
  if (err instanceof ConnectionClosed) {
    console.error("Closed:", err.code, err.reason, err.wasClean);
  } else if (err instanceof ConnectionError) {
    console.error("Connection error:", err.cause);
  } else if (err instanceof ErrorResponse) {
    console.error("Server error:", err.response.code, err.response.message);
  } else if (err instanceof StreamError) {
    console.error("Stream error:", err.errorCode, err.errorMessage);
  } else if (err instanceof ProtocolError) {
    console.error("Protocol error:", err.reason);
  }
}

Error Types:

  • ConnectionClosed - WebSocket closed (code, reason, wasClean)
  • ConnectionError - WebSocket error (cause)
  • ErrorResponse - Server rejected request (response)
  • StreamError - Stream ended with error (streamId, errorCode, errorMessage)
  • ProtocolError - Protocol error (reason, data) - report to developers

Examples

See the examples directory for complete working examples:

  • basic.ts - Complete example with swap quote streaming and price checking
  • middleware.ts - Middleware proxy for browser usage

Links

  • GitHub Repository: https://github.com/Titan-Pathfinder/titan-sdk-ts
  • API Documentation: https://titan-exchange.gitbook.io/titan/titan-developer-docs

License

MIT License - see LICENSE file for details.