Skip to content

Repository files navigation

Yellowstone Geyser gRPC Client

A fully-typed TypeScript client for the Solana Yellowstone Geyser gRPC service, providing real-time access to Solana blockchain data with complete type safety and @solana/web3.js compatibility.

Installation

npm install @igroza/yellowstone-grpc-client

Proto Files

Copy the gRPC proto files to your project's proto folder:

cp ./node_modules/@igroza/yellowstone-grpc-client/proto/* ./proto/

This will copy the required .proto files (geyser.proto and solana-storage.proto) to your project for gRPC connection.

Dependency Security

Production dependencies

These dependencies will not be installed in your project, but are included in the release bundle file via esbuild.

Dependency graph:

Production dependency graph

Development dependencies

These dependencies used for building the package.

Quick Start

Run example from repository

  • Clone the repository:

      git clone https://github.com/igroza/yellowstone-grpc-client.git
      cd yellowstone-grpc-client
  • replace grpc-url.com:10101 with your actual gRPC endpoint in the example/example.ts file

  • Install dependencies:

    npm install
  • Run example code:

    npm run example

Example code

import { YellowstoneGeyserClient, CommitmentLevel, TransactionFormatter } from '@igroza/yellowstone-grpc-client';

// Create client
const client = new YellowstoneGeyserClient({
  endpoint: 'grpc-url.com:10101',
  credentials: 'your-api-token' // optional
});

// Connect to server
await client.connect();

// Subscribe to transactions
const stream = client.createSubscription(
  {
    transactions: {
      'my_filter': {
        vote: false,
        failed: false,
        account_include: ['YourAccountAddressHere']
      }
    },
    commitment: CommitmentLevel.PROCESSED
  },
  (update) => {
    if (update.transaction) {
      const tx = TransactionFormatter.formTransactionFromJson(update);
      console.log('Transaction:', tx.transaction.signatures[0]);
    }
  },
  (error) => console.error('Error:', error),
  () => console.log('Stream ended')
);

API Reference

YellowstoneGeyserClient

Constructor

const client = new YellowstoneGeyserClient(config: YellowstoneGeyserClientConfig);

Configuration:

interface YellowstoneGeyserClientConfig {
  endpoint: string;              // gRPC endpoint
  credentials?: string;          // Optional authentication token (added to 'x-token' header)
  options?: grpc.ChannelOptions; // Optional gRPC channel options
}

Example with authentication:

const client = new YellowstoneGeyserClient({
  endpoint: 'grpc-url.com:10101',
  credentials: 'your-api-token-here'
});

Methods

connect(): Promise<void>

Establishes connection to the gRPC server.

await client.connect();
createSubscription(request, onData, onError?, onEnd?)

Creates a subscription with callback handlers for data, errors, and stream end.

const stream = client.createSubscription(
  { transactions: { 'filter': { vote: false } }, commitment: CommitmentLevel.PROCESSED },
  (update) => console.log('Update:', update),
  (error) => console.error('Error:', error),
  () => console.log('Stream ended')
);
subscribe(request: SubscribeRequest, options?: grpc.CallOptions)

Low-level method that creates a bidirectional streaming subscription. Returns a gRPC stream object.

const stream = client.subscribe({
  transactions: { 'filter': { vote: false } },
  commitment: CommitmentLevel.PROCESSED
});
getVersion(): Promise<GetVersionResponse>

Retrieves the Geyser server version information.

const version = await client.getVersion();
console.log('Version:', version.version);
getSlot(request?: GetSlotRequest): Promise<GetSlotResponse>

Gets the current slot number.

const slot = await client.getSlot();
console.log('Current slot:', slot.slot);
getBlockHeight(request?: GetBlockHeightRequest): Promise<GetBlockHeightResponse>

Gets the current block height.

const blockHeight = await client.getBlockHeight();
console.log('Block height:', blockHeight.block_height);
getLatestBlockhash(request?: GetLatestBlockhashRequest): Promise<GetLatestBlockhashResponse>

Gets the latest blockhash.

const { blockhash, slot, last_valid_block_height } = await client.getLatestBlockhash();
isBlockhashValid(request: IsBlockhashValidRequest): Promise<IsBlockhashValidResponse>

Checks if a blockhash is still valid.

const { valid } = await client.isBlockhashValid({ blockhash: 'your-blockhash' });
ping(request: PingRequest): Promise<PongResponse>

Sends a ping to test connectivity.

const response = await client.ping({ count: 1 });
sendPing(stream, id: number): void

Sends a ping through an existing subscription stream to keep the connection alive.

client.sendPing(stream, Date.now());
close(): void

Closes the gRPC client connection.

client.close();

Subscription Types

Account Subscriptions

Subscribe to account updates with flexible filtering options.

client.createSubscription(
  {
    accounts: {
      'my_accounts': {
        account: ['AccountPubkey1', 'AccountPubkey2'],
        owner: ['ProgramId1', 'ProgramId2'],
        filters: [
          { memcmp: { offset: 0, base58: 'SomeData' } },
          { datasize: 165 },
          { token_account_state: true },
          { lamports: { gt: 1000000 } }
        ],
        nonempty_txn_signature: true
      }
    },
    commitment: CommitmentLevel.CONFIRMED
  },
  (update) => {
    if (update.account) {
      const { account, slot } = update.account;
      console.log('Account updated:', {
        pubkey: Buffer.from(account.pubkey).toString('base64'),
        lamports: account.lamports,
        slot: slot
      });
    }
  }
);

Transaction Subscriptions

Subscribe to transactions with filtering options.

client.createSubscription(
  {
    transactions: {
      'my_transactions': {
        vote: false,
        failed: false,
        account_include: ['Account1', 'Account2'],
        account_exclude: ['Account3'],
        account_required: ['Account4']
      }
    },
    commitment: CommitmentLevel.PROCESSED,
    from_slot: 100000000
  },
  (update) => {
    if (update.transaction) {
      const tx = TransactionFormatter.formTransactionFromJson(update);
      console.log('Transaction:', {
        signature: tx.transaction.signatures[0],
        slot: tx.slot,
        success: tx.meta?.err === null
      });
    }
  }
);

Slot Subscriptions

Subscribe to slot status updates.

client.createSubscription(
  {
    slots: {
      'my_slots': {
        filter_by_commitment: true,
        interslot_updates: true
      }
    },
    commitment: CommitmentLevel.FINALIZED
  },
  (update) => {
    if (update.slot) {
      console.log('Slot:', update.slot.slot, 'Status:', update.slot.status);
    }
  }
);

Block Subscriptions

Subscribe to complete block data.

client.createSubscription(
  {
    blocks: {
      'my_blocks': {
        account_include: ['AccountToMonitor'],
        include_transactions: true,
        include_accounts: true,
        include_entries: true
      }
    },
    commitment: CommitmentLevel.CONFIRMED
  },
  (update) => {
    if (update.block) {
      console.log('Block:', update.block.slot, 'Txs:', update.block.executed_transaction_count);
    }
  }
);

Block Meta Subscriptions

Subscribe to lightweight block metadata.

client.createSubscription(
  {
    blocks_meta: { 'my_block_meta': {} },
    commitment: CommitmentLevel.FINALIZED
  },
  (update) => {
    if (update.block_meta) {
      console.log('Block:', update.block_meta.slot, update.block_meta.blockhash);
    }
  }
);

Entry Subscriptions

Subscribe to entry updates.

client.createSubscription(
  { entry: { 'my_entries': {} } },
  (update) => {
    if (update.entry) {
      console.log('Entry:', update.entry.slot, update.entry.index);
    }
  }
);

Transaction Status Subscriptions

Subscribe to lightweight transaction status updates.

client.createSubscription(
  {
    transactions_status: {
      'my_tx_status': {
        vote: false,
        failed: false
      }
    }
  },
  (update) => {
    if (update.transaction_status) {
      console.log('Status:', update.transaction_status.slot);
    }
  }
);

TransactionFormatter

Utility class for converting between Yellowstone transaction data and Solana web3.js compatible formats.

formTransactionFromJson(update: SubscribeUpdate): VersionedTransactionResponse

Converts raw Yellowstone transaction data into Solana web3.js VersionedTransactionResponse format.

import { TransactionFormatter } from '@igroza/yellowstone-grpc-client';

const tx = TransactionFormatter.formTransactionFromJson(update);
console.log('Signature:', tx.transaction.signatures[0]);
console.log('Fee:', tx.meta.fee);
console.log('Success:', tx.meta.err === null);
console.log('Slot:', tx.slot);
console.log('Block Time:', tx.blockTime);

toJSON(transaction: VersionedTransactionResponse): any

Converts a VersionedTransactionResponse to plain JSON format for serialization or storage.

import { TransactionFormatter } from '@igroza/yellowstone-grpc-client';

const tx = TransactionFormatter.formTransactionFromJson(update);
const json = TransactionFormatter.toJSON(tx);

// Store or transmit as JSON
console.log(JSON.stringify(json, null, 2));

// Save to file or database
fs.writeFileSync('transaction.json', JSON.stringify(json));

Update Types

Check the update_oneof field to determine update type:

import { UpdateType } from '@igroza/yellowstone-grpc-client';

client.createSubscription(request, (update) => {
  switch (update.update_oneof) {
    case UpdateType.ACCOUNT:
      console.log('Account:', update.account);
      break;
    case UpdateType.TRANSACTION:
      const tx = TransactionFormatter.formTransactionFromJson(update);
      console.log('Transaction:', tx);
      break;
    case UpdateType.SLOT:
      console.log('Slot:', update.slot);
      break;
    case UpdateType.PING:
      client.sendPing(stream, Date.now());
      break;
    case UpdateType.PONG:
      console.log('Pong:', update.pong?.id);
      break;
  }
});

Filtering

Memory Compare

filters: [{ memcmp: { offset: 32, base58: 'YourMintAddress' } }]

Data Size

filters: [{ datasize: 165 }]

Lamports

filters: [{ lamports: { gt: 1000000 } }] // gt, lt, eq, ne

Data Slicing

accounts_data_slice: [{ offset: 0, length: 32 }]

Commitment Levels

CommitmentLevel.PROCESSED  // Fastest, may be rolled back
CommitmentLevel.CONFIRMED  // Confirmed by supermajority
CommitmentLevel.FINALIZED  // Finalized, cannot be rolled back

Error Handling

client.createSubscription(
  request,
  (update) => { /* handle data */ },
  (error) => {
    console.error('Error:', error.message, error.code);
    // Common codes: 14 (UNAVAILABLE), 4 (DEADLINE_EXCEEDED), 13 (INTERNAL)
  },
  () => console.log('Stream ended')
);

// Graceful shutdown
process.on('SIGINT', () => {
  client.close();
  process.exit(0);
});

Event Handling

The client extends EventEmitter and emits various events during its lifecycle. Use the YellowstoneGeyserClientEvents enum for type-safe event handling:

import { YellowstoneGeyserClient, YellowstoneGeyserClientEvents } from '@igroza/yellowstone-grpc-client';

const client = new YellowstoneGeyserClient({ endpoint: 'grpc-url.com:10101' });

// Listen to lifecycle events
client.on(YellowstoneGeyserClientEvents.INITIALIZED, () => {
  console.log('Client initialized');
});

client.on(YellowstoneGeyserClientEvents.CONNECTED, () => {
  console.log('Connected to server');
});

client.on(YellowstoneGeyserClientEvents.SUBSCRIBED, (request) => {
  console.log('Subscription created:', request);
});

client.on(YellowstoneGeyserClientEvents.ERROR, (error) => {
  console.error('Error occurred:', error);
});

client.on(YellowstoneGeyserClientEvents.STREAM_ENDED, () => {
  console.log('Stream ended');
});

client.on(YellowstoneGeyserClientEvents.CLOSED, () => {
  console.log('Client closed');
});

client.on(YellowstoneGeyserClientEvents.STATUS, (status) => {
  console.log('Status update:', status);
});

Available Events:

  • INITIALIZED - Client has been initialized
  • CONNECTED - Successfully connected to the gRPC server
  • SUBSCRIBED - Subscription has been created
  • ERROR - An error occurred
  • STREAM_ENDED - Subscription stream has ended
  • CLOSED - Client connection has been closed
  • STATUS - gRPC status update received

License

MIT

About

A TypeScript client for the Solana Yellowstone Geyser gRPC service, providing real-time access to Solana blockchain data with full type safety.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages