> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbitflare.com/llms.txt
> Use this file to discover all available pages before exploring further.

# gRPC Client (Yellowstone)

> Yellowstone Geyser streaming with automatic reconnection, ping/pong liveness detection, and YAML config.

## Install

```bash theme={null}
npm install @orbitflare/sdk @grpc/grpc-js yaml
```

`@grpc/grpc-js` and `yaml` are optional peer dependencies. Install them only if you use the gRPC client (or YAML config).

## Building the client

Here's a client with every option set:

```ts theme={null}
import { GeyserClientBuilder } from '@orbitflare/sdk/grpc';
import type { RetryPolicy } from '@orbitflare/sdk';

const client = new GeyserClientBuilder()
  .url('http://ny.rpc.orbitflare.com:10000')
  .fallbackUrl('http://fra.rpc.orbitflare.com:10000')
  .retry({
    initialDelayMs: 100,
    maxDelayMs: 30_000,
    multiplier: 2.0,
    maxAttempts: 0,
  })
  .timeoutSecs(30)
  .keepaliveSecs(60)
  .pingIntervalSecs(10)
  .maxMissedPongs(3)
  .channelCapacity(4096)
  .build();
```

Minimal setup:

```ts theme={null}
const client = new GeyserClientBuilder()
  .url('http://ny.rpc.orbitflare.com:10000')
  .build();
```

### Builder methods

**`.url(url)`** - The primary gRPC endpoint. Falls back to `ORBITFLARE_GRPC_URL` env var.

```ts theme={null}
.url('http://ny.rpc.orbitflare.com:10000')
```

**`.urls([...])`** - Primary + fallbacks in one call. First element is primary.

```ts theme={null}
.urls(['http://ny.rpc.orbitflare.com:10000', 'http://fra.rpc.orbitflare.com:10000'])
```

**`.fallbackUrl(url)` / `.fallbackUrls([...])`** - Add fallback endpoints. On connection failure, the SDK rotates through them.

```ts theme={null}
.fallbackUrl('http://fra.rpc.orbitflare.com:10000')
```

**`.retry(policy)`** - Controls reconnection backoff. When a connection drops, the SDK waits `initialDelayMs`, then doubles it each attempt (capped at `maxDelayMs`). Set `maxAttempts` to 0 for infinite retries. Default: 100ms initial, 30s max, 2x multiplier, infinite.

```ts theme={null}
.retry({
  initialDelayMs: 200,
  maxDelayMs: 15_000,
  multiplier: 2.0,
  maxAttempts: 0,
})
```

**`.timeoutSecs(n)`** - Per-request gRPC timeout. Default: 30.

```ts theme={null}
.timeoutSecs(15)
```

**`.keepaliveSecs(n)`** - TCP keepalive interval. The OS sends probes at this interval to detect dead connections at the TCP level. Default: 60.

```ts theme={null}
.keepaliveSecs(30)
```

**`.pingIntervalSecs(n)`** - How often the SDK sends proto-level `Ping` messages to the server. The server should respond with a `Pong`. Default: 10.

```ts theme={null}
.pingIntervalSecs(15)
```

**`.maxMissedPongs(n)`** - How many consecutive pings can go unanswered before the SDK considers the connection dead and reconnects. Default: 3. With the defaults, a dead connection is detected within 30 seconds.

```ts theme={null}
.maxMissedPongs(5)
```

**`.channelCapacity(n)`** - The bounded buffer between the background task and your code. If your code is slow to consume events, the background task pauses when this fills up instead of eating unlimited memory. Default: 4096.

```ts theme={null}
.channelCapacity(8192)
```

## Writing a YAML config

Create a YAML file with the filters you want:

```yaml theme={null}
# grpc.yml
transactions:
  pumpfun:
    vote: false
    failed: false
    account_include:
      - "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
  jupiter:
    vote: false
    failed: false
    account_include:
      - "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"

accounts:
  usdc_mint:
    account:
      - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
  token_program:
    owner:
      - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"

slots:
  all:
    filter_by_commitment: true

commitment: confirmed
```

### YAML filter reference

**`transactions`** - each entry is a named filter. A transaction matches if it involves any of the `account_include` addresses. `account_exclude` removes matches. `account_required` means all listed addresses must be present. `vote` and `failed` filter by transaction type.

**`accounts`** - watch specific accounts by address, or watch all accounts owned by a program via `owner`.

**`slots`** - subscribe to slot updates. `filter_by_commitment` only sends updates at your commitment level.

**`commitment`** - `'processed'`, `'confirmed'`, or `'finalized'`. Defaults to `'confirmed'`.

The YAML supports `${ENV_VAR}` expansion:

```yaml theme={null}
transactions:
  target:
    account_include:
      - "${TARGET_PROGRAM}"
```

## Subscribing and reading events

### From YAML

```ts theme={null}
const stream = client.subscribeYaml('grpc.yml');
```

### Programmatically

For dynamic filters built at runtime:

```ts theme={null}
import { proto } from '@orbitflare/sdk/proto';

const request: proto.geyser.SubscribeRequest = {
  transactions: {
    target: {
      vote: false,
      failed: false,
      signature: undefined,
      accountInclude: [someAddress],
      accountExclude: [],
      accountRequired: [],
    },
  },
  accounts: {},
  slots: {},
  transactionsStatus: {},
  blocks: {},
  blocksMeta: {},
  entry: {},
  accountsDataSlice: [],
  commitment: 1,
  ping: { id: 1 },
};

const stream = client.subscribe(request);
```

### Reading the stream

Both `subscribeYaml` and `subscribe` return a `GeyserStream`. It's an async iterable - use `for await`:

```ts theme={null}
for await (const update of stream) {
  if (update.transaction) {
    // update.transaction.slot - the slot this transaction was in
    // update.transaction.transaction - the transaction info (signature, accounts, instructions, meta)
  } else if (update.account) {
    // update.account.slot - the slot
    // update.account.account - account info (pubkey, lamports, owner, data)
    // update.account.isStartup - true during initial account snapshot
  } else if (update.slot) {
    // update.slot.slot - the slot number
    // update.slot.status - processed, confirmed, finalized, etc.
  } else if (update.blockMeta) {
    // update.blockMeta.slot, update.blockMeta.blockhash, update.blockMeta.parentSlot
    // update.blockMeta.executedTransactionCount
  }
}
```

Or call `await stream.next()` directly to pull one event at a time. Returns `undefined` when the stream is closed. If retries are exhausted, the next iteration throws.

Pong messages are consumed internally and never appear in your stream.

### Closing

```ts theme={null}
stream.close();
```

Stops the background task immediately.

## Multiple streams

One client can run many streams at once. Each gets its own background connection:

```ts theme={null}
const pumpfun = client.subscribeYaml('config/pumpfun.yml');
const raydium = client.subscribeYaml('config/raydium.yml');
const slots = client.subscribeYaml('config/slots.yml');
```

They share endpoint health state - if one stream quarantines a failing endpoint, the others skip it on their next reconnect. But their connections and lifecycles are fully independent.

You can spin up new streams at any time, including dynamically based on data from an existing stream.

## Full example

A stream that watches pump.fun transactions, decodes the signature, and prints a summary for each one.

```ts theme={null}
import bs58 from 'bs58';
import { GeyserClientBuilder } from '@orbitflare/sdk/grpc';

async function main() {
  const client = new GeyserClientBuilder()
    .url('http://ny.rpc.orbitflare.com:10000')
    .fallbackUrl('http://fra.rpc.orbitflare.com:10000')
    .build();

  const stream = client.subscribeYaml('grpc.yml');
  let txCount = 0;

  console.log('streaming...');

  for await (const update of stream) {
    if (update.transaction) {
      txCount += 1;
      const info = update.transaction.transaction;
      if (info?.signature) {
        const sig = bs58.encode(info.signature);
        const fee = info.meta?.fee ?? 0n;
        const ixCount = info.transaction?.message?.instructions.length ?? 0;
        console.log(
          `#${txCount} slot=${update.transaction.slot} sig=${sig.slice(0, 16)}... fee=${fee} instructions=${ixCount}`,
        );
      }
    } else if (update.slot) {
      console.log(`slot ${update.slot.slot} (${update.slot.status})`);
    }
  }
}

void main();
```

With this `grpc.yml`:

```yaml theme={null}
transactions:
  pumpfun:
    vote: false
    failed: false
    account_include:
      - "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"

slots:
  all:
    filter_by_commitment: true

commitment: confirmed
```
