> For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/wire-protocol.md).

# Wire Protocol

This page covers how data is encoded on the wire — the serialization format, encoding conventions, and shared types used across all Titan API messages.

**Both Titan Direct and Titan Gateway use MessagePack binary encoding. JSON is not supported.**

## Data format

The basic data format for serialization of all messages is **MessagePack**.

* **Objects/structs are encoded as maps** — this allows additional fields to be added without breaking compatibility with previous versions.
* **Field names are `camelCase`** unless otherwise specified.
* **Integers are encoded using the smallest MessagePack int type** that fits the value.
* **Use `BigInt` for `u64` values** (amounts, timestamps) — values above 2^53 lose precision as float64, which the server rejects.

## Optional data

If a value is optional, its type is `Option<T>` in Rust and `T?` or `T | null` in TypeScript.

**Optional fields in objects may be omitted entirely from the serialized map.** Otherwise, a missing optional value should be encoded as `nil` (`0xc0`) — decoded as `None` in Rust and `null` in TypeScript.

## Simple enumerations

Simple enumerations (those without associated data) are **encoded as strings matching the variant name exactly**:

{% tabs %}
{% tab title="Rust" %}

```rust
enum SwapMode {
  ExactIn,
  ExactOut,
}
// Encoded as: "ExactIn" or "ExactOut"
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
enum SwapMode {
  ExactIn = "ExactIn",
  ExactOut = "ExactOut",
}
```

{% endtab %}
{% endtabs %}

## Complex enumerations

Complex enumerations (those with associated data) are **encoded as single-value maps**, mapping the variant name to the associated data.

* Single associated item → the value is that data.
* Multiple associated items → the value is an array.

{% tabs %}
{% tab title="Rust" %}

```rust
struct Request2Data {
  id: u32,
  amount: u64,
}

enum Complex {
  Request1(String),
  Request2(Request2Data),
  Request3(u32, u32),
}

// Valid encodings (shown as JSON for readability):
// { "Request1": "hello" }
// { "Request2": {"id": 1, "amount": 34} }
// { "Request3": [3, 4] }
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
interface Request2Data {
  id: number;
  amount: number;
}

type Complex =
  | { Request1: string }
  | { Request2: Request2Data }
  | { Request3: [number, number] };
```

{% endtab %}
{% endtabs %}

This pattern applies to both client requests (`RequestData`) and server messages (`ServerMessage`). **To determine the message type, check which key is present in the top-level map.**

## Binary data

Binary data is encoded using MessagePack `bin` formats.

{% tabs %}
{% tab title="Rust" %}

```rust
// Variable-sized byte array
Vec<u8>
// Fixed-size byte array (N bytes)
[u8; N]
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
// Binary data in TypeScript
Uint8Array
// or
ArrayBuffer
```

{% endtab %}
{% endtabs %}

TypeScript has no way to specify byte array size — refer to the Rust types for size constraints.

***

## Common types

### Pubkey

**Solana public keys are 32-byte binary data.** Encoded using MessagePack `bin 8` format — all pubkeys start with `c4 20` followed by 32 bytes of key data.

Example — the WSOL public key `So11111111111111111111111111111111111111112`:

```
c4 20 069b8857feab8184fb687f634618c035dac439dc1aeb3b5598a0f00000000001
```

{% tabs %}
{% tab title="Rust" %}

```rust
// Type alias for public keys
type Pubkey = [u8; 32];
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
// Type alias for public keys to differentiate from other binary data
type Pubkey = Uint8Array; // 32 bytes
```

{% endtab %}
{% endtabs %}

### AccountMeta

Compact account descriptor used in instructions. **Uses single-letter field names to minimize message size.**

{% tabs %}
{% tab title="Rust" %}

```rust
struct AccountMeta {
  p: Pubkey, // public key
  s: bool,   // is_signer
  w: bool,   // is_writable
}
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
interface AccountMeta {
  p: Pubkey;  // public key
  s: boolean; // is_signer
  w: boolean; // is_writable
}
```

{% endtab %}
{% endtabs %}

### Instruction

A single on-chain instruction. **Also uses single-letter field names for compactness.**

{% tabs %}
{% tab title="Rust" %}

```rust
struct Instruction {
  p: Pubkey,           // program_id
  a: Vec<AccountMeta>, // accounts
  d: Vec<u8>,          // data
}
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
interface Instruction {
  p: Pubkey;        // program_id
  a: AccountMeta[]; // accounts
  d: Uint8Array;    // data
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**`AccountMeta` and `Instruction` use single-letter field names (`p`, `s`, `w`, `a`, `d`) to reduce payload size.** These are Titan's wire format, not abbreviations of the standard Solana SDK types.
{% endhint %}

***

## Message envelope types

### ClientRequest

Every client request wraps an RPC method call with a monotonically increasing `id`.

{% tabs %}
{% tab title="Rust" %}

```rust
struct ClientRequest {
  /// Request ID, echoed in the server's response.
  id: u32,
  /// One of the RPC method variants.
  data: RequestData,
}

enum RequestData {
  GetInfo(GetInfoRequest),
  NewSwapQuoteStream(SwapQuoteRequest),
  StopStream(StopStreamRequest),
  GetVenues(GetVenuesRequest),
  ListProviders(ListProvidersRequest),
  GetSwapPrice(SwapPriceRequest),
}
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
interface ClientRequest {
  // Request ID, echoed in the server's response.
  id: number;
  // One of the RPC method variants.
  data: RequestData;
}

type RequestData =
  | { GetInfo: GetInfoRequest }
  | { NewSwapQuoteStream: SwapQuoteRequest }
  | { StopStream: StopStreamRequest }
  | { GetVenues: GetVenuesRequest }
  | { ListProviders: ListProvidersRequest }
  | { GetSwapPrice: SwapPriceRequest };
```

{% endtab %}
{% endtabs %}

### ServerMessage

**The server sends one of four message types:**

{% tabs %}
{% tab title="Rust" %}

```rust
/// A message sent by the server to the client.
enum ServerMessage {
  /// Successful response to a request, may optionally start a stream.
  Response(ResponseSuccess),
  /// An error response to a request.
  Error(ResponseError),
  /// Data for a stream.
  StreamData(StreamData),
  /// Notification that a stream has ended.
  StreamEnd(StreamEnd),
}

/// A successful response.
struct ResponseSuccess {
  /// Identifier of the request that triggered this response.
  requestId: u32,
  /// The response data.
  data: ResponseData,
  /// If this request starts a new stream, contains stream info.
  stream: Option<StreamStart>,
}

/// An error response.
struct ResponseError {
  /// Identifier of the request that triggered this response.
  requestId: u32,
  /// A numeric error code.
  code: u32,
  /// A message describing the error.
  message: String,
}

/// Data packet for a stream.
struct StreamData {
  /// ID of the stream.
  id: u32,
  /// Sequence number of this data packet.
  seq: u32,
  /// Data payload.
  payload: StreamDataPayload,
}

/// Notification that a stream has closed.
struct StreamEnd {
  /// ID of the stream that has ended.
  id: u32,
  /// Error code, if the stream ended abnormally.
  errorCode: Option<u32>,
  /// Error message, if the stream ended abnormally.
  errorMessage: Option<String>,
}

/// Notification that a new stream has been started.
struct StreamStart {
  /// Stream ID — present in all StreamData and StreamEnd for this stream.
  id: u32,
  /// Type of data that will be sent in this stream.
  dataType: StreamDataType,
}

enum StreamDataType {
  SwapQuotes,
  // May be expanded in the future.
}

enum StreamDataPayload {
  SwapQuotes(SwapQuotes),
  // May be expanded in the future.
}

enum ResponseData {
  GetInfo(ServerInfo),
  NewSwapQuoteStream(QuoteSwapStreamResponse),
  StreamStopped(StopStreamResponse),
  GetVenues(VenueInfo),
  ListProviders(Vec<ProviderInfo>),
  GetSwapPrice(SwapPrice),
}
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
type ServerMessage =
  | { Response: ResponseSuccess }
  | { Error: ResponseError }
  | { StreamData: StreamData }
  | { StreamEnd: StreamEnd };

interface ResponseSuccess {
  // Identifier of the request that triggered this response.
  requestId: number;
  // The response data.
  data: ResponseData;
  // If the request started a new stream, contains stream info.
  stream?: StreamStart;
}

interface ResponseError {
  // Identifier of the request that triggered this response.
  requestId: number;
  // A numeric error code.
  code: number;
  // A message describing the error.
  message: string;
}

interface StreamData {
  // ID of the stream.
  id: number;
  // Sequence number of this data packet.
  seq: number;
  // Data payload.
  payload: StreamDataPayload;
}

interface StreamEnd {
  // ID of the stream that has ended.
  id: number;
  // Error code, if the stream ended abnormally.
  errorCode?: number;
  // Error message, if the stream ended abnormally.
  errorMessage?: string;
}

interface StreamStart {
  // Stream ID.
  id: number;
  // Type of data that will be sent in this stream.
  dataType: StreamDataType;
}

enum StreamDataType {
  SwapQuotes = "SwapQuotes",
}

type StreamDataPayload = { SwapQuotes: SwapQuotes };

type ResponseData =
  | { GetInfo: ServerInfo }
  | { NewSwapQuoteStream: QuoteSwapStreamResponse }
  | { StreamStopped: StopStreamResponse }
  | { GetVenues: VenueInfo }
  | { ListProviders: ProviderInfo[] }
  | { GetSwapPrice: SwapPrice };
```

{% endtab %}
{% endtabs %}

***

## Compression

Compression wraps the MessagePack payload. The order of operations:

**Sending:** serialize to MessagePack → compress → send as binary WebSocket frame

**Receiving:** receive binary frame → decompress → deserialize from MessagePack

The compression scheme is negotiated once at connection time. See [Connection & Negotiation](/titan/developer-doc/swap-api/reference/direct/connection.md) for protocol strings and setup.

## Gateway differences

Titan Gateway uses the same MessagePack encoding but over HTTP REST:

* **Requests** — query parameters (pubkeys as Base58 strings, not binary)
* **Responses** — MessagePack body with `Content-Type: application/vnd.msgpack`
* **Pubkeys in responses** are still binary `Uint8Array` in the MessagePack body

***

## Related pages

* [Connection & Negotiation](/titan/developer-doc/swap-api/reference/direct/connection.md) — WebSocket setup, authentication, and compression negotiation
* [GetInfo](/titan/developer-doc/swap-api/reference/direct/get-info.md) — server settings and protocol version
* [NewSwapQuoteStream](/titan/developer-doc/swap-api/reference/direct/new-swap-quote-stream.md) — streaming swap quotes
